Запуск нескольких асинхронных задач и ожидание их завершения


Мне нужно запустить несколько асинхронных задач в консольном приложении, и ждать их завершения перед дальнейшей обработкой.

там много статей, но я, кажется, все больше запутываюсь, чем больше читаю. Я прочитал и понял основные принципы библиотеки задач, но мне явно не хватает ссылки где-то.

Я понимаю, что можно цеплять задачи так, чтобы они начинались после завершения другого (что в значительной степени является сценарием для всех статьи, которые я читал), но я хочу, чтобы все мои задачи выполнялись одновременно, и я хочу знать, как только они все будут завершены.

какая самая простая реализация для такого сценария?

6 170

6 ответов:

в обоих ответах не упоминалось ожидаемое Task.WhenAll:

var task1 = DoWorkAsync();
var task2 = DoMoreWorkAsync();

await Task.WhenAll(task1, task2);

основное различие между Task.WaitAll и Task.WhenAll это то, что первый будет блокировать (аналогично использованию Wait на одной задаче), в то время как последняя не будет и может быть ожидаема, отдавая контроль обратно вызывающему до завершения всех задач.

более того, обработка исключений отличается:

Task.WaitAll:

по крайней мере один из экземпляров задачи был отменен-или-исключение было вызвано во время выполнения по крайней мере одного из экземпляров задачи. Если задача была отменена, AggregateException содержит OperationCanceledException в своей коллекции InnerExceptions.

Task.WhenAll:

если какая-либо из поставленных задач завершается в неисправном состоянии, возвращенная задача также завершится в неисправном состоянии, где ее исключения будут содержать агрегацию из набора развернутых исключений из каждой поставляемой задачи.

если ни одна из поставленных задач не была сброшена, но хотя бы одна из них была отменена, возвращенная задача завершится в отмененном состоянии.

если ни одна из задач не была сброшена и ни одна из задач не была отменена, результирующая задача завершится в состоянии RanToCompletion. Если предоставленный массив/перечисляемый не содержит задач, возвращенная задача немедленно перейдет в состояние RanToCompletion до он возвращается к звонящему.

вы можете создать много задач, таких как:

List<Task> TaskList = new List<Task>();
foreach(...)
{
   var LastTask = new Task(SomeFunction);
   LastTask.Start();
   TaskList.Add(LastTask);
}

Task.WaitAll(TaskList.ToArray());

лучший вариант, который я видел, это следующий метод расширения:

public static Task ForEachAsync<T>(this IEnumerable<T> sequence, Func<T, Task> action) {
    return Task.WhenAll(sequence.Select(action));
}

назовем это так:

await sequence.ForEachAsync(item => item.SomethingAsync(blah));

или с асинхронной лямбда:

await sequence.ForEachAsync(async item => {
    var more = await GetMoreAsync(item);
    await more.FrobbleAsync();
});

вы хотите, чтобы цепь Tasks, или они могут быть вызваны параллельно?

объединение
Просто сделайте что-то вроде

Task.Run(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);
Task.Factory.StartNew(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);

и не забудьте проверить предыдущие Task экземпляра в каждом ContinueWith как это может быть нарушенными.

для параллельного образом
Самый простой метод, с которым я столкнулся: Parallel.Invoke В противном случае есть Task.WaitAll или вы даже можете использовать WaitHandles для выполнения обратного отсчета до нуля действий слева (подождите, есть новый класс:CountdownEvent), или ...

можно использовать WhenAll который вернет ожидаемый Task или WaitAll который не имеет возвращаемого типа и будет блокировать дальнейшее выполнение кода simular to Thread.Sleep пока все задачи не будут выполнены, отменены или неисправны.

enter image description here

пример

var tasks = new Task[] {
    await TaskOperationOne(),
    await TaskOperationTwo()
};

Task.WaitAll(tasks);
// or
await Task.WhenAll(tasks);

если вы хотите запускать задачи в пратикулярном порядке, вы можете получить форму вдохновения этой ответить.

вот как я это делаю с массивом Func:

var tasks = new Func<Task>[]
{
   () => myAsyncWork1(),
   () => myAsyncWork2(),
   () => myAsyncWork3()
};

await Task.WhenAll(tasks.Select(task => task()).ToArray()); //Async    
Task.WaitAll(tasks.Select(task => task()).ToArray()); //Or use WaitAll for Sync