Parallel.ForEach()
背後的全部想法是,你有一組線程,每個線程處理集合的一部分。正如您注意到的,這不適用於async
- await
,您希望在異步調用期間釋放該線程。
您可以通過阻止ForEach()
線程來「修復」,但是這會破壞整個點async
- await
。
你可以做的是使用TPL Dataflow而不是Parallel.ForEach()
,它支持異步Task
。
具體來說,您的代碼可以使用TransformBlock
來編寫,它使用async
lambda將每個id轉換爲Customer
。該塊可以配置爲並行執行。您可以將該塊鏈接到ActionBlock
,該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()
異步添加項目,例如,如果該集合太大。
與您的代碼相比(如果有效的話),作爲一個額外的好處是隻要單個項目完成就可以開始寫入,而不是等到所有處理完成。
一個非常簡要概述,反應性擴展,TPL和TPL數據流 - http://vantsuyoshi.wordpress.com/2012/01/05/when-to-use-tpl-async-reactive-extension-tpl-dataflow /對於像我這樣可能需要一些清晰度的人。 – 2013-09-13 11:04:54
我很確定這個答案不會並行處理。我相信你需要在id上做一個Parallel.ForEach並將它們發佈到getCustomerBlock。至少這是我在測試這個建議時發現的。 – JasonLind 2015-12-16 22:23:26
@JasonLind它確實如此。並行使用'Parallel.ForEach()''Post()'項目不應該有任何實際效果。 – svick 2015-12-16 22:26:02