Ожидание завершения группы задач
Задача: есть несколько задач, и нужно подождать, пока они все завершатся.
Решение
Для этой цели существует метод
Task.WhenAll. Он получает несколько задач и возвращает задачу, которая завершается при завершении всех указанных задач.Task task1 = Task.Delay(100);Если все задачи имеют одинаковый тип результата и все завершаются успешно, то задача
Task task2 = Task.Delay(200);
Task task3 = Task.Delay(150);
await Task.WhenAll(task1, task2, task3);
Task.WhenAll возвращает массив, содержащий результаты всех задач:Task<int> task1 = Task.FromResult(3);Есть перегруженная версия
Task<int> task2 = Task.FromResult(5);
Task<int> task3 = Task.FromResult(7);
int[] results = await Task.WhenAll(task1, task2, task3);
// "results" содержит { 3, 5, 7 }
Task.WhenAll, которая получает IEnumerable с задачами. Тем не менее заметьте, что при использовании LINQ код получается более понятным, когда последовательность явно материализуется:async Task<string> GetAllAsync(Если одна из задач выбросит исключение, то
HttpClient client,
IEnumerable<string> urls)
{
// Определяем действие для каждого URL
var downloads = urls.Select(url => client.GetStringAsync(url));
// Задачи ещё не запущены, т.к.
// последовательность не материализована
// Запускаем загрузку для всех URL одновременно
Task<string>[] downloadTasks = downloads.ToArray();
// Все задачи запущены.
// Асинхронно ожидаем завершения всех загрузок
string[] htmlPages =
await Task.WhenAll(downloadTasks);
return string.Concat(htmlPages);
}
Task.WhenAll вернёт задачу в статусе Faulted с этим исключением. Если сразу несколько задач выдают исключения, то все они помещаются в задачу, возвращаемую Task.WhenAll. При ожидании этой задачи будет выдано только одно из них:tryОбычно первого «попавшегося» исключения достаточно, но, если вам нужны все исключения, проверьте свойство
{
await Task.WhenAll(task1, task2);
}
catch (Exception ex)
{
// "ex" – исключение либо из task1, либо из task2
...
}
Exception задачи, возвращаемой Task.WhenAll:Task allTasks = Task.WhenAll(task1, task2);Если ни одна задача не выбросила исключения, но хотя бы одна задача была отменена, то задача, возвращённая
try
{
await Task.WhenAll(allTasks);
}
catch
{
AggregateException allExceptions = allTasks.Exception;
...
}
Task.WhenAll, будет в статусе Canceled.Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 2.