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