1

我有使用异步方法从多个来源加载数据的代码(如下所示)。

public class ThEnvironment
{
    public async Task LoadLookupsAsync(CancellationToken cancellationToken) { ... }
    public async Task LoadFoldersAsync(CancellationToken cancellationToken) { ... }
}

在我的程序的其他地方,我想加载多个环境。

ThEnvironment[] workingEnvironments = ThEnvironment.Manager.GetWorkingEnvironments();
foreach ( ThEnvironment environment in workingEnvironments )
{
    await environment.LoadLookupsAsync(CancellationToken.None);
    await environment.LoadFoldersAsync(CancellationToken.None);
}

我的问题是双重的:

  1. 对于任何单一环境,我将如何并行运行两个 environment.Load...Async() 方法并且仍然能够传递 CancellationToken?
  2. 我将如何为所有环境并行执行此操作?

底线:我想为所有环境并行运行所有 Load..Async() 方法,并且仍然能够传递 CancellationToken。


更新

感谢大家的快速解答。我已将代码缩减为以下内容:

Refresh();

var tasks = ThEnvironment.Manager.GetWorkingEnvironments()
    .SelectMany(e => new Task[] { e.LoadLookupsAsync(CancellationToken.None), e.LoadFoldersAsync(CancellationToken.None) });

await Task.WhenAll(tasks.ToArray());

我不太关心捕获所有异常,因为这不是生产应用程序。顶部的 Refresh() 处理了表单的初始绘制问题。

到处都是很棒的东西。

4

2 回答 2

2

您可以简单地启动两个Tasks,然后等待它们完成:

Task lookupsTask = environment.LoadLookupsAsync(cancellationToken);
Task foldersTask = environment.LoadFoldersAsync(cancellationToken);

await lookupsTask;
await foldersTask;

相同代码的更有效版本是使用Task.WhenAll()

await Task.WhenAll(lookupsTask, foldersTask);

如果您想对Tasks 集合执行此操作,而不仅仅是其中两个,您可以List<Task>用所有Tasks 和 then填充 a await Task.WhenAll(tasksList)

这种方法的一个问题是,如果发生多个异常,您只会得到第一个异常。如果这对您来说是个问题,您可以使用类似 280Z28 建议的东西。

于 2013-11-08T17:15:36.317 回答
1

使用相同的取消令牌启动这两个操作,然后等待它们完成。

Task lookupsTask = environment.LoadLookupsAsync(cancellationToken);
Task foldersTask = environment.LoadFoldersAsync(cancellationToken);

await Task.Factory.ContinueWhenAll(
    new[] { lookupsTask, foldersTask },
    TaskExtrasExtensions.PropagateExceptions,
    TaskContinuationOptions.ExecuteSynchronously);

这使用了PropagateExceptionsParallel Extensions Extras 示例代码中的扩展方法来确保来自加载任务的异常信息不会丢失:

/// <summary>Propagates any exceptions that occurred on the specified tasks.</summary>
/// <param name="tasks">The Task instances whose exceptions are to be propagated.</param>
public static void PropagateExceptions(this Task [] tasks)
{
    if (tasks == null) throw new ArgumentNullException("tasks");
    if (tasks.Any(t => t == null)) throw new ArgumentException("tasks");
    if (tasks.Any(t => !t.IsCompleted)) throw new InvalidOperationException("A task has not completed.");
    Task.WaitAll(tasks);
}
于 2013-11-08T16:21:11.733 回答