La idea Parallel.ForEach()es que tienes un conjunto de hilos y cada hilo procesa parte de la colección. Como notó, esto no funciona con async- await, donde desea liberar el hilo durante la duración de la llamada asincrónica.
Podrías "arreglarlo" bloqueando los ForEach()hilos, pero eso derrota todo el punto de async- await.
Lo que podría hacer es usar TPL Dataflow en lugar de hacerlo Parallel.ForEach(), que también admite asíncronos Task.
Específicamente, su código podría escribirse usando un TransformBlockque transforma cada identificación en un Customeruso de asynclambda. Este bloque se puede configurar para ejecutarse en paralelo. Debería vincular ese bloque a uno ActionBlockque escriba cada uno Customeren la consola. Después de configurar la red de bloque, puede Post()cada identificación a TransformBlock.
En codigo:
var ids = new List<string> { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" };
var getCustomerBlock = new TransformBlock<string, Customer>(
async i =>
{
ICustomerRepo repo = new CustomerRepo();
return await repo.GetCustomer(i);
}, new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded
});
var writeCustomerBlock = new ActionBlock<Customer>(c => Console.WriteLine(c.ID));
getCustomerBlock.LinkTo(
writeCustomerBlock, new DataflowLinkOptions
{
PropagateCompletion = true
});
foreach (var id in ids)
getCustomerBlock.Post(id);
getCustomerBlock.Complete();
writeCustomerBlock.Completion.Wait();
Aunque probablemente desee limitar el paralelismo de la TransformBlocka una pequeña constante. Además, puede limitar la capacidad de los elementos TransformBlocky agregarlos de forma asincrónica SendAsync(), por ejemplo, si la colección es demasiado grande.
Como un beneficio adicional en comparación con su código (si funcionó) es que la escritura comenzará tan pronto como termine un solo elemento, y no espere hasta que todo el procesamiento haya finalizado.