c# - tiempo - Ejecutar varias tareas asíncronas y esperar a que se completen todas
programación asíncrona con c# pdf (6)
Necesito ejecutar varias tareas asíncronas en una aplicación de consola y esperar a que se completen todas antes de continuar el proceso.
Hay muchos artículos por ahí, pero parece que estoy más confundido cuanto más leo. He leído y entiendo los principios básicos de la biblioteca de tareas, pero claramente me falta un enlace en alguna parte.
Entiendo que es posible encadenar tareas para que comiencen después de que se complete otra (que es prácticamente el escenario para todos los artículos que he leído), pero quiero que todas mis tareas se ejecuten al mismo tiempo, y quiero saber una vez todos están completos.
¿Cuál es la implementación más simple para un escenario como este?
¿Desea encadenar las Task
, o pueden invocarse de forma paralela?
Para encadenar
Solo haz algo como
Task.Run(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);
Task.Factory.StartNew(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);
y no olvide verificar la instancia de la Task
anterior en cada ContinueWith
ya que podría tener una falla.
Para la forma paralela
El método más simple que encontré: Parallel.Invoke
De lo contrario, hay Task.WaitAll
o incluso puedes usar WaitHandle
s para hacer una cuenta atrás para poner en cero las acciones restantes (espera, hay una nueva clase: CountdownEvent
), o ...
Así es como lo hago con una matriz 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
La mejor opción que he visto es el siguiente método de extensión:
public static Task ForEachAsync<T>(this IEnumerable<T> sequence, Func<T, Task> action) {
return Task.WhenAll(sequence.Select(action));
}
Llámalo así:
await sequence.ForEachAsync(item => item.SomethingAsync(blah));
O con una lambda asíncrona:
await sequence.ForEachAsync(async item => {
var more = await GetMoreAsync(item);
await more.FrobbleAsync();
});
Podrías crear muchas tareas como:
List<Task> TaskList = new List<Task>();
foreach(...)
{
var LastTask = new Task(SomeFunction);
LastTask.Start();
TaskList.Add(LastTask);
}
Task.WaitAll(TaskList.ToArray());
Puede usar WhenAll
que devolverá una Task
o WaitAll
que no tenga ningún tipo de retorno y bloqueará la ejecución de código simular a Thread.Sleep
hasta que todas las tareas se completen, cancelen o fallen.
Ejemplo
var tasks = new Task[] {
await TaskOperationOne(),
await TaskOperationTwo()
};
Task.WaitAll(tasks);
// or
await Task.WhenAll(tasks);
Si desea ejecutar las tareas en un orden particular, puede obtener inspiración de this manual.
Ambas respuestas no mencionaron la Tarea Task.WhenAll
Cuando Task.WhenAll
:
var task1 = DoWorkAsync();
var task2 = DoMoreWorkAsync();
await Task.WhenAll(task1, task2);
La principal diferencia entre Task.WaitAll
y Task.WhenAll
es que el primero se bloqueará (similar al uso de Wait
en una sola tarea) mientras que el último no se puede esperar y se puede esperar, cediendo el control a la persona que llama hasta que todas las tareas finalicen.
Más aún, el manejo de excepciones difiere:
Task.WaitAll
. Task.WaitAll
:
Al menos una de las instancias de la tarea se canceló -o bien- se lanzó una excepción durante la ejecución de al menos una de las instancias de la tarea. Si se canceló una tarea, AggregateException contiene una OperationCanceledException en su colección InnerExceptions.
Task.WhenAll
. Task.WhenAll
:
Si alguna de las tareas suministradas se completa en un estado en falla, la tarea devuelta también se completará en un estado Faulted, donde sus excepciones contendrán la agregación del conjunto de excepciones no envueltas de cada una de las tareas suministradas.
Si ninguna de las tareas provistas tuvo una falla, pero al menos una de ellas fue cancelada, la tarea devuelta terminará en el estado Cancelado.
Si ninguna de las tareas presentó un error y ninguna de las tareas se canceló, la tarea resultante finalizará en el estado RanToCompletion. Si la matriz proporcionada / enumerable no contiene tareas, la tarea devuelta pasará inmediatamente a un estado RanToCompletion antes de que se devuelva a la persona que llama.