Ответ
Для ожидания завершения группы задач используйте статические методы Task.WaitAll (блокирующий) или Task.WhenAll (асинхронный).
1. Task.WaitAll(Task[]) — синхронное (блокирующее) ожидание.
Останавливает текущий поток до завершения всех задач. Используйте в синхронном коде, но избегайте в UI-потоках или асинхронных методах, чтобы не вызывать deadlock.
Task[] tasks = new Task[3];
tasks[0] = Task.Run(() => DoWork(1));
tasks[1] = Task.Run(() => DoWork(2));
tasks[2] = Task.Run(() => DoWork(3));
// Текущий поток блокируется здесь
Task.WaitAll(tasks);
Console.WriteLine("Все задачи завершены.");
2. await Task.WhenAll(Task[]) — асинхронное ожидание.
Предпочтительный способ в асинхронных методах (async). Не блокирует поток, а освобождает его для других операций.
public async Task ProcessAllDataAsync()
{
Task<int>[] downloadTasks = new Task<int>[5];
for (int i = 0; i < downloadTasks.Length; i++)
{
downloadTasks[i] = DownloadDataAsync(i);
}
// Ожидаем завершения всех задач, не блокируя поток
int[] results = await Task.WhenAll(downloadTasks);
// Когда все задачи завершены, работаем с результатами
int sum = results.Sum();
Console.WriteLine($"Общая сумма: {sum}");
}
Критически важная деталь: Обработка исключений.
Task.WaitAllиawait Task.WhenAllпри ошибке в любой из задач выбрасываютAggregateException(в .NET Core 3.0+WaitAllможет выбросить первое исключение).- Правильный подход — проверять статус каждой задачи:
try { await Task.WhenAll(tasks); } catch // Ловим первое исключение, но AggregateException могло быть "развернуто" { // Игнорируем здесь, проверяем задачи ниже }
// Анализируем каждую задачу на наличие ошибок foreach (var task in tasks) { if (task.IsFaulted) { // task.Exception - это AggregateException, содержащее реальные ошибки Console.WriteLine($"Задача завершилась с ошибкой: {task.Exception?.InnerException?.Message}"); } }
Используйте `WhenAll` для асинхронного кода и всегда планируйте обработку частичных сбоев.