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 Taskdobrze obsługuje asynchroniczne s.
Konkretnie, twój kod może być napisany przy użyciu, TransformBlockktóry przekształca każdy identyfikator w Customerprzy użyciu asynclambda. Blok ten można skonfigurować do wykonywania równoległego. Łączyłbyś ten blok z pismem, ActionBlockktóry zapisuje każdy Customerdo 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 TransformBlockmałej stałej. Ponadto możesz ograniczyć pojemność TransformBlocki 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.