แนวคิดเบื้องหลังทั้งหมดParallel.ForEach()คือคุณมีชุดของเธรดและแต่ละเธรดจะประมวลผลส่วนหนึ่งของคอลเลกชัน ตามที่คุณสังเกตเห็นสิ่งนี้ไม่สามารถใช้งานได้async- awaitซึ่งคุณต้องการปล่อยเธรดในระหว่างการโทรแบบ async
คุณสามารถ“แก้ไข” ที่โดยการปิดกั้นForEach()หัวข้อ แต่ที่เอาชนะจุดรวมของ-asyncawait
สิ่งที่คุณจะทำคือการใช้TPL DataflowแทนParallel.ForEach()ซึ่งสนับสนุนตรงกันTasks ดี
โดยเฉพาะอย่างยิ่งรหัสของคุณสามารถเขียนได้โดยใช้การTransformBlockแปลงแต่ละ id เป็นการCustomerใช้asyncแลมบ์ดา บล็อกนี้สามารถกำหนดค่าให้ดำเนินการแบบขนาน คุณจะเชื่อมโยงบล็อกนั้นกับสิ่งActionBlockที่เขียนแต่ละรายการCustomerไปยังคอนโซล หลังจากที่คุณตั้งค่าเครือข่ายบล็อกที่คุณสามารถPost()แต่ละ ID 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()ตัวอย่างเช่นถ้าคอลเลกชันมีขนาดใหญ่เกินไป
ในฐานะที่เป็นประโยชน์เพิ่มเติมเมื่อเปรียบเทียบกับรหัสของคุณ (ถ้ามันทำงาน) คือการเขียนจะเริ่มทันทีที่รายการเดียวเสร็จและไม่รอจนกว่าการประมวลผลทั้งหมดจะเสร็จสิ้น