假设我有两个资源A
和B
,我想通过MVVM(这= =查看模型)向我的用户显示这两个资源
this.A = GetA();
this.B = GetB();
一旦我开始使用TPL:
this.A = await GetAAsync();
this.B = await GetBAsync();
这开始得到A
。当A
准备好后,它会显示A
并继续对B
执行相同操作 - 这不是一个非常好的解决方案。会更好:
var taskA = GetAAsync();
var taskB = GetBAsync();
this.A = await taskA;
this.B = await taskB;
现在,这开始获得A
,开始获得B
并等待A
。当A
准备好后,它会显示A
并等待B
,直到显示出来。 看起来不错,但,如果A
有时需要花费更多时间来加载B
会怎样?
我如何实现以下方案:
A
。B
。答案 0 :(得分:6)
最简单的选择就是将异步操作与更新本身相结合,然后您可以启动并等待两个操作,但是当每个操作完成时,它会更新它所需的内容。你可以通过"坚持"每个方法结束时的更新或创建一个像这样的异步包装器方法:
async Task UpdateAAsync()
{
A = await GetAAsync();
}
async Task UpdateBAsync()
{
B = await GetBAsync();
}
await Task.WhenAll(UpdateAAsync(), UpdateBAsync());
或者,正如svick建议的那样,您可以使用包装器lambda表达式而不是包装器方法:
Func<Task> updateAAsync = async () => A = await GetAAsync();
Func<Task> updateBAsync = async () => B = await GetBAsync();
await Task.WhenAll(updateAAsync(), updateBAsync());
答案 1 :(得分:2)
您可以分别安排这两项任务的延续。
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
GetAAsync().ContinueWith(t => this.A = t.Result, uiScheduler);
GetBAsync().ContinueWith(t => this.B = t.Result, uiScheduler);
这样,如果这是一个WinForms或WPF应用程序,则会将延期分派给UI线程。
另请注意,我没有为调用线程编写任何代码来等待这两个任务完成。如果您要阻止调用线程(不推荐)并且该线程碰巧是UI线程,那么您将遇到死锁情况:
var taskA = GetAAsync().ContinueWith(t => this.A = t.Result, uiScheduler);
var taskB = GetBAsync().ContinueWith(t => this.B = t.Result, uiScheduler);
Task.WaitAll(taskA, taskB);
最后一行将阻止UI线程。当任务尝试在UI线程(被阻止)上分派延续时,这将阻止任务完成。
答案 2 :(得分:2)
您可以将Task
类的静态方法与await结合使用以防止阻塞接口:
var taskA = GetAAsync();
var taskB = GetBAsync();
await Task.WhenAny(new [] { taskA,taskB });
//first task completed
await Task.WhenAll(new [] { taskA,taskB });
//all tasks completed
如果有更多的任务而不是只有两个任务,这个解决方案会变得更复杂,那么你应该寻找一个Task.WhenAll
符合你需要的解决方案,或者使用像Backgroundworker这样的东西。
答案 3 :(得分:0)
MSDN上有一篇文章讨论了这个问题。 Read it here。
基本上,您使用WhenAny
的结果来查看已完成的Task
,并将其从包含任务的列表中删除。然后重复,直到列表为空:
IEnumerable<Task<int>> downloadTasksQuery = ListOfTasks();
while (downloadTasks.Count > 0)
{
Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks);
downloadTasks.Remove(firstFinishedTask);
// process the result from firstFinishedTask
}
如果有超过2个任务,这也可以扩展。