[英]Parallel.ForEach with async lambda waiting forall iterations to complete
最近我看到了幾個與 Parallel.ForEach 相關的 SO 線程與異步 lambdas 混合,但所有建議的答案都是某種解決方法。
有什么辦法可以寫:
List<int> list = new List<int>[]();
Parallel.ForEach(arrayValues, async (item) =>
{
var x = await LongRunningIoOperationAsync(item);
list.Add(x);
});
如何確保列表將包含在每次迭代中使用 lambda 執行的所有迭代中的所有項目?
Parallel.ForEach 通常如何與異步 lambdas 一起工作,如果它命中等待,它會將其線程移交給下一次迭代嗎?
我認為 ParallelLoopResult IsCompleted 字段不正確,因為它會在執行所有迭代時返回 true,無論它們的實際 lambda 作業是否完成?
最近我看到了幾個與 Parallel.ForEach 相關的 SO 線程與異步 lambdas 混合,但所有建議的答案都是某種解決方法。
嗯,那是因為Parallel
不適用於async
。 從不同的角度來看,您首先為什么要混合它們? 他們做相反的事情。 Parallel
就是添加線程, async
就是放棄線程。 如果您想同時進行異步工作,請使用Task.WhenAll
。 這是完成這項工作的正確工具; Parallel
不是。
也就是說,聽起來您想使用錯誤的工具,所以這是您的操作方法...
如何確保列表將包含在每次迭代中使用 lambda 執行的所有迭代中的所有項目?
您需要有某種信號,某些代碼可以在處理完成之前阻止該信號,例如, CountdownEvent
或Monitor
。 附帶說明一下,您還需要保護對非線程安全List<T>
。
Parallel.ForEach 通常如何與異步 lambdas 一起工作,如果它命中等待,它會將其線程移交給下一次迭代嗎?
由於Parallel
不理解async
lambda,當第一個await
產生(返回)給它的調用者時, Parallel
將假定循環的交互已經完成。
我認為 ParallelLoopResult IsCompleted 字段不正確,因為它會在執行所有迭代時返回 true,無論它們的實際 lambda 作業是否完成?
正確的。 據Parallel
所知,它只能“看到”返回給調用者的第一個await
的方法。 所以它不知道async
lambda 何時完成。 它還會假設迭代過早完成,這會導致分區中斷。
你不需要Parallel.For/ForEach
在這里你只需要等待任務列表。
背景
簡而言之,您需要非常小心異步 lambdas ,如果您將它們傳遞給Action
或Func<Task>
您的問題是因為Parallel.For / ForEach
不適合async 和 await 模式或IO 綁定任務。 它們適用於受CPU 限制的工作負載。 這意味着它們本質上具有Action
參數,讓任務調度程序為您創建任務
如果你想同時運行多個異步任務,使用Task.WhenAll
,或者一個TPL 數據流塊(或類似的東西),它可以有效地處理CPU 綁定和IO 綁定的工作負載,或者更直接地說,它們可以處理任務就是異步方法。
除非您需要在 lambda 內部執行更多操作(您尚未顯示),否則只需使用Select
和WhenAll
var tasks = items .Select(LongRunningIoOperationAsync);
var results = await Task.WhenAll(tasks); // here is your list of int
如果你這樣做,你仍然可以使用等待,
var tasks = items.Select(async (item) =>
{
var x = await LongRunningIoOperationAsync(item);
// do other stuff
return x;
});
var results = await Task.WhenAll(tasks);
注意:如果您需要Parallel.ForEach
的擴展功能(即控制最大並發的選項),有多種方法,但 RX 或 DataFlow 可能是最簡潔的
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.