我有一个方法,可以从cloudDrive下载一个memoryStream,然后将此memoryStream传递给taskList,该任务将读取文件:
public async Task CreateFiles(List<MyDataClass> list)
{
var tasks = new List<Task>();
foreach (var item in list)
{
var memorySteam = await _cloudService.DownloadStreamFromCloudAsync(item);
tasks.Add(Task.Run(() =>
{
using (memoryStream)
{
ReadFile readFile = new ReadFile(memoryStream, new MyFileSerializer());
readFile.DoWork();
}
UpdateTreeViewRecursively();
}));
}
}
public void UpdateTreeViewRecursively()
{
//...
//Here I do recursive operations on a treeView. This may take a while
}
问题在于,当一个任务完成并读取文件时,我调用了一个方法,该方法完成了一些耗时的工作,并且有可能该方法尚未完成,而下一个任务已在再次调用它。我应该使用锁还是针对此问题的其他解决方案?
编辑: 我将尝试解释为什么我选择了这种架构:
1)用户调用CreateFiles方法并开始从云下载:30个文件,大小从1mb到70mb不等。一次只能下载一个文件。
2)然后将每个下载的memoryStream传递到task.Add(..)。通过这样做,我希望能够同时读取很少的文件,并且在完成一个文件时,我调用UpdateTreeViewRecursively();。此方法将treeViewItem标记为可以使用,并且还检查其父级的所有其他childItem是否已下载并可以使用。这样,用户可以立即看到可以使用哪些文件,而不必等到处理了70mb的大文件后才能开始使用程序。
3)为了确保正确设置treeView中的标志,我需要确保UpdateTreeViewRecursively();在前一个调用者执行该方法时,不会再调用该方法。
答案 0 :(得分:1)
问题在于,当一个任务完成并读取文件时,我调用了一个方法,该方法完成了一些耗时的工作,并且有可能该方法尚未完成,而下一个任务已在再次调用它。我应该使用锁还是针对此问题的其他解决方案?
由于该方法是修改UI线程,因此应在UI线程上运行。所以也许是这样的:
public async Task CreateFiles(List<MyDataClass> list)
{
var tasks = list.Select(async item =>
{
using (var memorySteam = await _cloudService.DownloadStreamFromCloudAsync(item))
{
await Task.Run(() =>
{
ReadFile readFile = new ReadFile(memoryStream, new MyFileSerializer());
readFile.DoWork();
});
}
UpdateTreeViewRecursively();
});
}
UI上下文本身充当“锁”,因此不会同时运行UpdateTreeViewRecursively
。
如果“这可能要花一些时间”代码使用户体验降级,则应重构该方法,以便它可以将速度较慢的部分卸载到Task.Run
,或者对所有部分使用IProgress<T>
其用户界面会更新,并在UpdateTreeViewRecursively
中运行整个Task.Run
方法。例子:
// Approach using Task.Run within UpdateTreeViewRecursively:
public async Task UpdateTreeViewRecursively()
{
UpdateSomeTreeViewItem();
var data = await Task.Run(() => SomeSlowCodeThatDoesNotUpdateUI());
UpdateSomeOtherTreeViewItem(data);
}
// Approach using IProgress<T>:
public async Task CreateFiles(List<MyDataClass> list)
{
var tasks = list.Select(async item =>
{
using (var memorySteam = await _cloudService.DownloadStreamFromCloudAsync(item))
{
await Task.Run(() =>
{
ReadFile readFile = new ReadFile(memoryStream, new MyFileSerializer());
readFile.DoWork();
});
}
var progress = new Progress<MyTreeViewItemUpdate>(update => UpdateSomeTreeViewItem(update));
await Task.Run(() => UpdateTreeViewRecursively();
});
}
public void UpdateTreeViewRecursively(IProgress<MyTreeViewItemUpdate> progress)
{
progress?.Report(new MyTreeViewItemUpdate() { ... });
var data = SomeSlowCodeThatDoesNotUpdateUI();
progress?.Report(new MyTreeViewItemUpdate() { ... = data... });
}
答案 1 :(得分:0)
在Task.Run方法上使用锁或使用.Result来等待任务的结果。这样,您的主线程将等待直到任务完成。