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 Task
dengan baik.
Secara khusus, kode Anda dapat ditulis menggunakan TransformBlock
yang mengubah setiap id menjadi Customer
menggunakan async
lambda. Blok ini dapat dikonfigurasi untuk dieksekusi secara paralel. Anda akan menautkan blok itu ke ActionBlock
yang 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 TransformBlock
ke beberapa konstanta kecil. Selain itu, Anda dapat membatasi kapasitas TransformBlock
dan 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.