我有使用异步方法从多个源加载数据的代码(如下所示)。
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);
}
我的问题有两个:
底线:我想并行运行所有环境的所有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()处理了表单的初始绘制问题。
各种各样的好东西。
答案 0 :(得分:2)
你可以简单地启动两个Task
,然后等待它们两个完成:
Task lookupsTask = environment.LoadLookupsAsync(cancellationToken);
Task foldersTask = environment.LoadFoldersAsync(cancellationToken);
await lookupsTask;
await foldersTask;
相同代码的更高效版本是使用Task.WhenAll()
:
await Task.WhenAll(lookupsTask, foldersTask);
如果你想为Task
的集合执行此操作,而不只是其中两个,你可以用List<Task>
填充所有Task
,然后填充{{1} }。
这种方法的一个问题是,如果发生多个异常,那么你只会获得第一个异常。如果这对您来说是个问题,您可以使用280Z28建议的内容。
答案 1 :(得分:1)
使用相同的取消令牌启动两个操作,然后等待它们都完成。
Task lookupsTask = environment.LoadLookupsAsync(cancellationToken);
Task foldersTask = environment.LoadFoldersAsync(cancellationToken);
await Task.Factory.ContinueWhenAll(
new[] { lookupsTask, foldersTask },
TaskExtrasExtensions.PropagateExceptions,
TaskContinuationOptions.ExecuteSynchronously);
这使用Parallel Extensions Extras示例代码中的PropagateExceptions
扩展方法来确保加载任务中的异常信息不会丢失:
/// <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);
}