Task.WhenAll

  • 如果需要执行几个Task,等待他们全部完成,可以使用Task.WhenAll方法。
  1. //创建一个任务,该任务将在数组中的所有 Task 对象都已完成时完成。
  2. public static Task WhenAll(params Task[] tasks);

下示简单例子:

  1. Task task1 = Task.Delay(TimeSpan.FromSeconds(1));
  2. Task task2 = Task.Delay(TimeSpan.FromSeconds(2));
  3. Task task3 = Task.Delay(TimeSpan.FromSeconds(1));
  4. await Task.WhenAll(task1, task2, task3);

如果所有任务的结果类型相同,并且全部成功地完成,则 Task.WhenAll 返回存有每个任务执行结果的数组:

  1. Task task1 = Task.FromResult(3);
  2. Task task2 = Task.FromResult(5);
  3. Task task3 = Task.FromResult(7);
  4. int[] results = await Task.WhenAll(task1, task2, task3);
  5. // "results" 含有 { 3, 5, 7 }

Task.WhenAll 方法有以 IEnumerable 类型作为参数的重载,但最好不要使用。只要异步代码与 LINQ 结合,显式的“具体化”序列(即对序列求值,创建集合)就会使代码更清晰:

  1. static async Task<string> DownloadAllAsync(IEnumerable<string> urls)
  2. {
  3. var httpClient = new HttpClient();
  4. // 定义每一个 url 的使用方法。
  5. var downloads = urls.Select(url => httpClient.GetStringAsync(url));
  6. // 注意,到这里,序列还没有求值,所以所有任务都还没真正启动。
  7. // 下面,所有的 URL 下载同步开始。
  8. Task<string>[] downloadTasks = downloads.ToArray();
  9. // 到这里,所有的任务已经开始执行了。
  10. // 用异步方式等待所有下载完成。
  11. string[] htmlPages = await Task.WhenAll(downloadTasks);
  12. return string.Concat(htmlPages);
  13. }

如果有一个任务抛出异常,则 Task.WhenAll 会出错,并把这个异常放在返回的 Task 中。如果多个任务抛出异常,则这些异常都会放在返回的 Task 中。

但是,如果这个 Task 在被await 调用,就只会抛出其中的一个异常。如果要得到每个异常,可以检查 Task.WhenALl返回的 Task 的 Exception 属性:

  1. static async Task ThrowNotImplementedExceptionAsync()
  2. {
  3. throw new NotImplementedException();
  4. }
  5. static async Task ThrowInvalidOperationExceptionAsync()
  6. {
  7. throw new InvalidOperationException();
  8. }
  9. static async Task ObserveOneExceptionAsync()
  10. {
  11. var task1 = ThrowNotImplementedExceptionAsync();
  12. var task2 = ThrowInvalidOperationExceptionAsync();
  13. try
  14. {
  15. await Task.WhenAll(task1, task2);
  16. }
  17. catch (Exception ex)
  18. {
  19. // ex 要么是 NotImplementedException,要么是 InvalidOperationException
  20. }
  21. }
  22. static async Task ObserveAllExceptionsAsync()
  23. {
  24. var task1 = ThrowNotImplementedExceptionAsync();
  25. var task2 = ThrowInvalidOperationExceptionAsync();
  26. Task allTasks = Task.WhenAll(task1, task2);
  27. try
  28. {
  29. await allTasks;
  30. }
  31. catch
  32. {
  33. //如果要得到每个异常,可以检查 Task.WhenALl返回的 Task 的 Exception 属性:
  34. AggregateException allExceptions = allTasks.Exception;
  35. }
  36. }