Основная идея Parallel.ForEach()
заключается в том, что у вас есть набор потоков, и каждый поток обрабатывает часть коллекции. Как вы заметили, это не работает с async
- await
, где вы хотите освободить поток на время асинхронного вызова.
Вы можете «исправить» это, заблокировав ForEach()
потоки, но это разрушает весь смысл async
- await
.
То, что вы могли бы сделать, это использовать поток данных TPL вместо Parallel.ForEach()
, который Task
хорошо поддерживает асинхронные s.
В частности, ваш код может быть написан с использованием, TransformBlock
который преобразует каждый идентификатор в Customer
использование async
лямбда-выражения. Этот блок может быть настроен для параллельного выполнения. Вы бы связали этот блок с тем, ActionBlock
что записывает каждый Customer
на консоль. После того, как вы настроите блочную сеть, вы можете Post()
каждый идентификатор в TransformBlock
.
В коде:
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();
Хотя вы, вероятно, хотите ограничить параллельность TransformBlock
некоторой небольшой константой. Кроме того, вы можете ограничить емкость TransformBlock
и добавлять элементы к нему асинхронно SendAsync()
, например, если коллекция слишком большая.
Дополнительным преимуществом по сравнению с вашим кодом (если он работал) является то, что запись начнется, как только закончится отдельный элемент, а не будет ждать завершения всей обработки.