L'idée derrière Parallel.ForEach()est que vous avez un ensemble de threads et que chaque thread traite une partie de la collection. Comme vous l'avez remarqué, cela ne fonctionne pas avec async- await, où vous souhaitez libérer le thread pour la durée de l'appel asynchrone.
Vous pouvez «corriger» cela en bloquant les ForEach()threads, mais cela va à l'encontre de tout l'intérêt de async- await.
Ce que vous pouvez faire, c'est utiliser TPL Dataflow à la place de Parallel.ForEach(), qui prend Taskbien en charge les s asynchrones .
Plus précisément, votre code peut être écrit en utilisant un TransformBlockqui transforme chaque identifiant en un en Customerutilisant le asynclambda. Ce bloc peut être configuré pour s'exécuter en parallèle. Vous lieriez ce bloc à un ActionBlockqui écrit chacun Customersur la console. Après avoir configuré le réseau de blocage, vous pouvez Post()chaque identifiant sur le TransformBlock.
Dans du code:
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();
Bien que vous souhaitiez probablement limiter le parallélisme du TransformBlockà une petite constante. En outre, vous pouvez limiter la capacité du TransformBlocket y ajouter les éléments de manière asynchrone en utilisant SendAsync(), par exemple, si la collection est trop grande.
Un avantage supplémentaire par rapport à votre code (s'il a fonctionné) est que l'écriture commencera dès qu'un seul élément est terminé, et n'attendra pas que tout le traitement soit terminé.