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 TransformBlock
que transforma cada identificación en un Customer
uso de async
lambda. Este bloque se puede configurar para ejecutarse en paralelo. Debería vincular ese bloque a uno ActionBlock
que escriba cada uno Customer
en 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 TransformBlock
a una pequeña constante. Además, puede limitar la capacidad de los elementos TransformBlock
y 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.