whenall parallel example async c# async-await task-parallel-library parallel.foreach

c# - example - Parallel.ForEach y async-await



parallel foreach c# (3)

Ah, está bien. Creo que sé lo que está pasando ahora. async method => un "vacío asíncrono" que es "disparar y olvidar" (no recomendado para nada más que los manejadores de eventos). Esto significa que la persona que llama no puede saber cuándo se completa ... Por lo tanto, GetResult regresa mientras la operación aún se está ejecutando. Aunque los detalles técnicos de mi primera respuesta son incorrectos, el resultado es el mismo aquí: GetResult está regresando mientras las operaciones iniciadas por ForEach todavía se están ejecutando. Lo único que realmente podría hacer es await el Process (para que el lambda ya no sea async ) y esperar a que Process complete cada iteración. Pero, eso utilizará al menos un subproceso de agrupación de subprocesos para hacer eso y, por lo tanto, enfatizará ligeramente el grupo, probablemente haciendo uso de ForEach inútil. Simplemente no usaría Parallel.ForEach ...

Tenía ese método:

public async Task<MyResult> GetResult() { MyResult result = new MyResult(); foreach(var method in Methods) { string json = await Process(method); result.Prop1 = PopulateProp1(json); result.Prop2 = PopulateProp2(json); } return result; }

Entonces decidí usar Parallel.ForEach :

public async Task<MyResult> GetResult() { MyResult result = new MyResult(); Parallel.ForEach(Methods, async method => { string json = await Process(method); result.Prop1 = PopulateProp1(json); result.Prop2 = PopulateProp2(json); }); return result; }

Pero ahora tengo un error:

Un módulo o controlador asíncrono completado mientras que una operación asincrónica todavía estaba pendiente.


Alternativamente, con el paquete AsyncEnumerator NuGet puede hacer esto:

using System.Collections.Async; public async Task<MyResult> GetResult() { MyResult result = new MyResult(); await Methods.ParallelForEachAsync(async method => { string json = await Process(method); result.Prop1 = PopulateProp1(json); result.Prop2 = PopulateProp2(json); }, maxDegreeOfParallelism: 10); return result; }

donde ParallelForEachAsync es un método de extensión.


async no funciona bien con ForEach . En particular, su async lambda se está convirtiendo a un método async void . Hay una serie de razones para evitar el async void (como lo describo en un artículo de MSDN); uno de ellos es que no puede detectar fácilmente cuándo se completó la lambda async . ASP.NET verá el código devuelto sin completar el método de async void y (apropiadamente) emitirá una excepción.

Lo que probablemente quiera hacer es procesar los datos al mismo tiempo , pero no en paralelo . El código paralelo casi nunca debería usarse en ASP.NET. Así es como se vería el código con el procesamiento simultáneo asincrónico:

public async Task<MyResult> GetResult() { MyResult result = new MyResult(); var tasks = Methods.Select(method => ProcessAsync(method)).ToArray(); string[] json = await Task.WhenAll(tasks); result.Prop1 = PopulateProp1(json[0]); ... return result; }