Task.WhenAll
- 如果需要执行几个Task,等待他们全部完成,可以使用Task.WhenAll方法。
//创建一个任务,该任务将在数组中的所有 Task 对象都已完成时完成。
public static Task WhenAll(params Task[] tasks);
下示简单例子:
Task task1 = Task.Delay(TimeSpan.FromSeconds(1));
Task task2 = Task.Delay(TimeSpan.FromSeconds(2));
Task task3 = Task.Delay(TimeSpan.FromSeconds(1));
await Task.WhenAll(task1, task2, task3);
如果所有任务的结果类型相同,并且全部成功地完成,则 Task.WhenAll 返回存有每个任务执行结果的数组:
Task task1 = Task.FromResult(3);
Task task2 = Task.FromResult(5);
Task task3 = Task.FromResult(7);
int[] results = await Task.WhenAll(task1, task2, task3);
// "results" 含有 { 3, 5, 7 }
Task.WhenAll 方法有以 IEnumerable 类型作为参数的重载,但最好不要使用。只要异步代码与 LINQ 结合,显式的“具体化”序列(即对序列求值,创建集合)就会使代码更清晰:
static async Task<string> DownloadAllAsync(IEnumerable<string> urls)
{
var httpClient = new HttpClient();
// 定义每一个 url 的使用方法。
var downloads = urls.Select(url => httpClient.GetStringAsync(url));
// 注意,到这里,序列还没有求值,所以所有任务都还没真正启动。
// 下面,所有的 URL 下载同步开始。
Task<string>[] downloadTasks = downloads.ToArray();
// 到这里,所有的任务已经开始执行了。
// 用异步方式等待所有下载完成。
string[] htmlPages = await Task.WhenAll(downloadTasks);
return string.Concat(htmlPages);
}
如果有一个任务抛出异常,则 Task.WhenAll 会出错,并把这个异常放在返回的 Task 中。如果多个任务抛出异常,则这些异常都会放在返回的 Task 中。
但是,如果这个 Task 在被await 调用,就只会抛出其中的一个异常。如果要得到每个异常,可以检查 Task.WhenALl返回的 Task 的 Exception 属性:
static async Task ThrowNotImplementedExceptionAsync()
{
throw new NotImplementedException();
}
static async Task ThrowInvalidOperationExceptionAsync()
{
throw new InvalidOperationException();
}
static async Task ObserveOneExceptionAsync()
{
var task1 = ThrowNotImplementedExceptionAsync();
var task2 = ThrowInvalidOperationExceptionAsync();
try
{
await Task.WhenAll(task1, task2);
}
catch (Exception ex)
{
// ex 要么是 NotImplementedException,要么是 InvalidOperationException
}
}
static async Task ObserveAllExceptionsAsync()
{
var task1 = ThrowNotImplementedExceptionAsync();
var task2 = ThrowInvalidOperationExceptionAsync();
Task allTasks = Task.WhenAll(task1, task2);
try
{
await allTasks;
}
catch
{
//如果要得到每个异常,可以检查 Task.WhenALl返回的 Task 的 Exception 属性:
AggregateException allExceptions = allTasks.Exception;
}
}