Cała idea Parallel.ForEach()
polega na tym, że masz zestaw wątków, a każdy wątek przetwarza część kolekcji. Jak zauważyłeś, nie działa to z async
- await
, gdzie chcesz zwolnić wątek na czas trwania wywołania asynchronicznego.
Możesz to „naprawić”, blokując ForEach()
wątki, ale to niweczy cały punkt async
- await
.
Zamiast tego możesz użyć przepływu danych TPLParallel.ForEach()
, który Task
dobrze obsługuje asynchroniczne s.
Konkretnie, twój kod może być napisany przy użyciu, TransformBlock
który przekształca każdy identyfikator w Customer
przy użyciu async
lambda. Blok ten można skonfigurować do wykonywania równoległego. Łączyłbyś ten blok z pismem, ActionBlock
który zapisuje każdy Customer
do konsoli. Po skonfigurowaniu sieci bloków możesz Post()
przypisać każdy identyfikator do TransformBlock
.
W kodzie:
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();
Chociaż prawdopodobnie chcesz ograniczyć paralelizm tej TransformBlock
małej stałej. Ponadto możesz ograniczyć pojemność TransformBlock
i dodać do niej elementy asynchronicznie SendAsync()
, na przykład, jeśli kolekcja jest zbyt duża.
Dodatkową korzyścią w porównaniu z twoim kodem (jeśli zadziałał) jest to, że pisanie rozpocznie się, gdy tylko pojedynczy element zostanie zakończony, i nie czekaj, aż całe przetwarzanie zostanie zakończone.