等待在GUI中显示多个任务

时间:2014-09-19 11:42:35

标签: c# .net silverlight task-parallel-library async-await

假设我有两个资源AB,我想通过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
  • 当其中一个准备就绪时,请显示它。
  • 当另一个准备就绪时,也要显示它。

4 个答案:

答案 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个任务,这也可以扩展。