it-roy-ru.com

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

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

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

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

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

216
Daniel Minnaar

В обоих ответах не упоминается ожидаемое 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:

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

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

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

363
Yuval Itzchakov

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

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

Task.WaitAll(TaskList.ToArray());
91
Virus

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

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();
});
23
me22

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

enter image description here

Пример

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

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

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

16
NtFreX

Вы хотите связать Tasks или они могут вызываться параллельно?

Для создания цепочки
Просто сделай что-то вроде

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

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

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

8
Andreas Niedermair

Вот как я это делаю с массивом 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
4
DalSoft

Еще один ответ ... но я обычно нахожусь в случае, когда мне нужно одновременно загрузить данные и поместить их в переменные, например:

var cats = new List<Cat>();
var dog = new Dog();

var loadDataTasks = new Task[]
{
    Task.Run(async () => cats = await LoadCatsAsync()),
    Task.Run(async () => dog = await LoadDogAsync())
};

try
{
    await Task.WhenAll(loadDataTasks);
}
catch (Exception ex)
{
    // handle exception
}
3
Yehor Hromadskyi