Seluruh ide di belakang Parallel.ForEach()adalah bahwa Anda memiliki satu set utas dan setiap utas memproses bagian dari koleksi. Seperti yang Anda perhatikan, ini tidak berfungsi async- await, di mana Anda ingin melepaskan utas selama panggilan async.
Anda bisa "memperbaikinya" dengan memblokir ForEach()utas, tetapi itu mengalahkan seluruh titik async- await.
Apa yang Anda bisa lakukan adalah dengan menggunakan TPL Dataflow bukan Parallel.ForEach(), yang mendukung asynchronous Taskdengan baik.
Secara khusus, kode Anda dapat ditulis menggunakan TransformBlockyang mengubah setiap id menjadi Customermenggunakan asynclambda. Blok ini dapat dikonfigurasi untuk dieksekusi secara paralel. Anda akan menautkan blok itu ke ActionBlockyang menulis masing Customer- masing ke konsol. Setelah Anda mengatur jaringan blokir, Anda dapat Post()setiap id ke TransformBlock.
Dalam kode:
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();
Meskipun Anda mungkin ingin membatasi paralelisme TransformBlockke beberapa konstanta kecil. Selain itu, Anda dapat membatasi kapasitas TransformBlockdan menambahkan item ke asynchronous menggunakan SendAsync(), misalnya jika koleksi terlalu besar.
Sebagai manfaat tambahan bila dibandingkan dengan kode Anda (jika berhasil) adalah bahwa penulisan akan dimulai segera setelah satu item selesai, dan tidak menunggu sampai semua pemrosesan selesai.