c# programación - Ejecutar varias tareas asíncronas y esperar a que se completen todas




mismo al (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?


Answers

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

¿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

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.


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.


En mi archivo Xamarin Forms Xaml utilicé las siguientes líneas en el encabezado (etiqueta ContentPage) y funcionó perfectamente como quería.

Basicamente ahora

  • El intellisense muestra los campos en el enlace.
  • mi Resharper puede cambiar el nombre del enlace en el archivo Xaml si refactorizo ​​el nombre de la propiedad

    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:vm="clr-namespace:YourApplicationName.ViewModels;assembly=YourApplicationName"
    mc:Ignorable="d"
    d:DataContext="{d:DesignInstance {x:Type vm:CurrentPageViewModel}}"




c# .net asynchronous task-parallel-library async-await