Как можно дождаться выполнения всех задач из массива задач (Task[]) в C#?

«Как можно дождаться выполнения всех задач из массива задач (Task[]) в C#?» — вопрос из категории Многопоточность, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Для ожидания завершения группы задач используйте статические методы 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` для асинхронного кода и всегда планируйте обработку частичных сбоев.