Как можно дождаться выполнения всех задач из массива задач?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Ожидание выполнения всех задач в C#
В C# для ожидания выполнения всех задач из массива или коллекции существует несколько эффективных подходов. Выбор конкретного метода зависит от версии .NET и требуемой логики обработки.
Основные методы ожидания
1. Task.WaitAll() (синхронное ожидание)
Блокирует текущий поток до завершения всех задач. Этот подход следует использовать с осторожностью, так как он может привести к взаимоблокировкам в UI-приложениях.
Task[] tasks = new Task[3]
{
Task.Run(() => DoWork(1)),
Task.Run(() => DoWork(2)),
Task.Run(() => DoWork(3))
};
// Блокирующее ожидание всех задач
Task.WaitAll(tasks);
Console.WriteLine("Все задачи завершены");
2. Task.WhenAll() (асинхронное ожидание)
Создает задачу, которая завершается после выполнения всех предоставленных задач. Это предпочтительный подход в асинхронном программировании.
async Task ProcessAllTasksAsync()
{
Task[] tasks = new Task[3]
{
DoWorkAsync(1),
DoWorkAsync(2),
DoWorkAsync(3)
};
// Асинхронное ожидание без блокировки потока
await Task.WhenAll(tasks);
Console.WriteLine("Все задачи завершены");
}
3. Task.WhenAll() с возвращаемыми значениями
Если задачи возвращают результаты, можно использовать типизированную версию:
async Task ProcessWithResultsAsync()
{
Task<int>[] tasks = new Task<int>[]
{
GetResultAsync(1),
GetResultAsync(2),
GetResultAsync(3)
};
// Ожидаем завершения всех задач и получаем результаты
int[] results = await Task.WhenAll(tasks);
Console.WriteLine($"Сумма результатов: {results.Sum()}");
}
Дополнительные возможности
Обработка исключений
При использовании Task.WhenAll() исключения из всех задач агрегируются в AggregateException:
async Task HandleExceptionsAsync()
{
Task[] tasks = new Task[]
{
Task.Run(() => { throw new InvalidOperationException("Ошибка 1"); }),
Task.Run(() => { throw new ArgumentException("Ошибка 2"); })
};
try
{
await Task.WhenAll(tasks);
}
catch (AggregateException ae)
{
// Обработка всех исключений
foreach (var ex in ae.InnerExceptions)
{
Console.WriteLine($"Исключение: {ex.Message}");
}
}
}
Ожидание с таймаутом
async Task WaitWithTimeoutAsync()
{
Task[] tasks = new Task[]
{
DoLongWorkAsync(1),
DoLongWorkAsync(2)
};
// Создаем задачу таймаута
Task timeoutTask = Task.Delay(5000);
// Ожидаем завершения либо всех задач, либо таймаута
Task completedTask = await Task.WhenAny(Task.WhenAll(tasks), timeoutTask);
if (completedTask == timeoutTask)
{
Console.WriteLine("Превышено время ожидания");
// Можно отменить задачи через CancellationToken
}
}
Использование с CancellationToken
async Task ProcessWithCancellationAsync()
{
using var cts = new CancellationTokenSource();
cts.CancelAfter(3000); // Автоотмена через 3 секунды
var tasks = new List<Task>();
for (int i = 0; i < 5; i++)
{
tasks.Add(DoWorkWithCancellationAsync(i, cts.Token));
}
try
{
await Task.WhenAll(tasks);
}
catch (OperationCanceledException)
{
Console.WriteLine("Операция отменена");
}
}
Рекомендации по использованию
Когда использовать Task.WhenAll():
- В асинхронных методах (помеченных
async) - В приложениях с UI, чтобы не блокировать основной поток
- Когда требуется продолжение обработки после завершения всех задач
Когда использовать Task.WaitAll():
- В синхронном коде, где нельзя использовать
async/await - В тестовых методах или консольных приложениях
- При необходимости гарантировать завершение задач перед продолжением
Производительность и ограничения:
Task.WhenAll()создает новую задачу-обертку- При большом количестве задач (тысячи) рассмотрите использование
Parallel.ForEachили пакетной обработки - Для динамически добавляемых задач используйте
List<Task>с последующим вызовомTask.WhenAll()
Важное замечание: Всегда обрабатывайте исключения при ожидании нескольких задач, так как одна неудачная задача не должна препятствовать обработке исключений из других задач.
Выбор между Task.WaitAll() и Task.WhenAll() в основном зависит от контекста выполнения: синхронный код требует блокирующего ожидания, в то время как асинхронные сценарии выигрывают от неблокирующего подхода.