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

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

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

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

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

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. Если поставляемый массив / enumerable не содержит задач, возвращаемая задача немедленно переходит в состояние 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