run parallel method library example create await async c# .net asynchronous async-await task-parallel-library

method - task parallel library c#



Ejecutando tareas en paralelo. (5)

Casi nunca debes usar el constructor de Task directamente. En su caso, esa tarea solo activa la tarea real que no puede esperar.

Simplemente puede llamar a DoWork y recuperar una tarea, almacenarla en una lista y esperar a que todas las tareas se completen. Sentido:

tasks.Add(DoWork()); // ... await Task.WhenAll(tasks);

Sin embargo, los métodos asíncronos se ejecutan de forma síncrona hasta que se alcanza la primera espera en una tarea incompleta. Si te preocupa que esa parte tarde demasiado, usa Task.Run para descargarla en otro hilo de ThreadPool y luego guarda esa tarea en la lista:

tasks.Add(Task.Run(() => DoWork())); // ... await Task.WhenAll(tasks);

Bien, básicamente tengo un montón de tareas (10) y quiero comenzarlas todas al mismo tiempo y esperar a que se completen. Cuando termine quiero ejecutar otras tareas. Leí un montón de recursos sobre esto, pero no puedo entenderlo bien para mi caso particular ...

Esto es lo que tengo actualmente (el código ha sido simplificado):

public async Task RunTasks() { var tasks = new List<Task> { new Task(async () => await DoWork()), //and so on with the other 9 similar tasks } Parallel.ForEach(tasks, task => { task.Start(); }); Task.WhenAll(tasks).ContinueWith(done => { //Run the other tasks }); } //This function perform some I/O operations public async Task DoWork() { var results = await GetDataFromDatabaseAsync(); foreach (var result in results) { await ReadFromNetwork(result.Url); } }

Entonces, mi problema es que cuando estoy esperando que las tareas se completen con la llamada WhenAll , me dice que todas las tareas han terminado, aunque ninguna de ellas se haya completado. Intenté agregar Console.WriteLine en mi foreach y cuando foreach a la tarea de continuación, los datos que recibí de mis Task anteriores no han terminado.

¿Qué estoy haciendo mal aquí?


El método DoWork es un método de E / S asíncrono. Esto significa que no necesita varios subprocesos para ejecutar varios de ellos, ya que la mayoría de las veces el método esperará de forma asíncrona a que se complete la E / S. Un hilo es suficiente para hacer eso.

public async Task RunTasks() { var tasks = new List<Task> { DoWork(), //and so on with the other 9 similar tasks }; await Task.WhenAll(tasks); //Run the other tasks }

Casi nunca debe utilizar el constructor de Task para crear una nueva tarea. Para crear una tarea de E / S asíncrona, simplemente llame al método async . Para crear una tarea que se ejecutará en un subproceso de grupo de subprocesos, use Task.Run . Puede leer este artículo para obtener una explicación detallada de Task.Run y otras opciones para crear tareas.


Esencialmente estás mezclando dos paradigmas asíncronos incompatibles; es decir, Parallel.ForEach() y async-await .

Por lo que quieras, haz una o la otra. Por ejemplo, puede usar Parallel.For[Each]() y eliminar el async-await por completo. Parallel.For[Each]() solo regresará cuando todas las tareas paralelas estén completas, y luego puede pasar a las otras tareas.

El código tiene algunos otros problemas también:

  • marca el método asíncrono pero no lo espera (la espera que tiene está en el delegado, no en el método);

  • es casi seguro que desee .ConfigureAwait(false) en sus .ConfigureAwait(false) , especialmente si no está tratando de usar los resultados inmediatamente en un subproceso de la interfaz de usuario.


Si desea ejecutar el paralelo de esas tareas en diferentes subprocesos utilizando TPL, es posible que necesite algo como esto:

public async Task RunTasks() { var tasks = new List<Func<Task>> { DoWork, //... }; await Task.WhenAll(tasks.AsParallel().Select(async task => await task())); //Run the other tasks }

Estos se acercan a la paralelización de solo una pequeña cantidad de código: la puesta en cola del método al grupo de subprocesos y el retorno de una Task incompleta. Además, para una cantidad tan pequeña de tareas, la paralelización puede llevar más tiempo que simplemente ejecutarse de forma asíncrona. Esto podría tener sentido solo si sus tareas realizan un trabajo más prolongado (sincrónico) antes de su primera espera.

Para la mayoría de los casos mejor manera será:

public async Task RunTasks() { await Task.WhenAll(new [] { DoWork(), //... }); //Run the other tasks }

A mi opinión en su código:

  1. No debe ajustar su código en Task antes de pasar a Parallel.ForEach .

  2. Solo puede await Task.WhenAll lugar de usar ContinueWith .


Solo agrega también un bloque try-catch alrededor de la Tarea. Cuando todo

NB: se lanza una instancia de System.AggregateException que actúa como una envoltura alrededor de una o más excepciones que han ocurrido. Esto es importante para los métodos que coordinan múltiples tareas como Task.WaitAll () y Task.WaitAny () para que la excepción AggregateException pueda envolver todas las excepciones dentro de las tareas en ejecución que se han producido.

try { Task.WaitAll(tasks.ToArray()); } catch(AggregateException ex) { foreach (Exception inner in ex.InnerExceptions) { Console.WriteLine(String.Format("Exception type {0} from {1}", inner.GetType(), inner.Source)); } }