我希望這段代碼需要 1 秒才能執行:
public async void Test()
{
DateTime start = DateTime.Now;
await Parallel.ForEachAsync(new int[1000], new ParallelOptions { MaxDegreeOfParallelism = 1000 }, async (i, token) =>
{
Thread.Sleep(1000);
});
Console.WriteLine("End program: " (DateTime.Now - start).Seconds " seconds elapsed.");
}
相反,在我的電腦上需要 37 秒(i7-9700 8 核 8 執行緒):
End program: 37 seconds elapsed.
我正在生成 1000 個任務MaxDegreeOfParallelism = 1000......為什么它們不同時運行?
uj5u.com熱心網友回復:
該Parallel.ForEachAsync方法呼叫執行緒body上的異步委托。ThreadPool通常這個委托ValueTask會快速回傳,但在你的情況下,這不是發生的情況,因為你的委托并不是真正的異步:
async (i, token) => Thread.Sleep(1000);
您可能會在這里收到關于async缺少await運算子的方法的編譯器警告。盡管如此,為該方法提供混合的同步/異步作業負載Parallel.ForEachAsync是可以的。此方法旨在處理任何型別的作業負載。但如果作業負載大部分是同步的,則結果可能是飽和的ThreadPool.
當ThreadPool它已經創建了SetMinThreads方法指定的執行緒數時,稱為飽和,默認情況下等于Environment.ProcessorCount,并且需要完成更多的作業。在這種情況下,ThreadPool切換到一種保守演算法,每秒創建一個新執行緒(從 .NET 6 開始)。此行為未準確記錄,并且可能在未來的 .NET 版本中發生變化。
為了獲得您想要的行為,即并行運行所有 1000 個輸入的委托,您必須增加ThreadPool按需立即創建的執行緒數:
ThreadPool.SetMinThreads(1000, 1000); // At the start of the program
有人會說,這樣做之后,您將不再擁有執行緒池,因為執行緒池意味著是一個可重用執行緒的小型池。但是,如果您不關心語意并且只想完成作業,那么無論后果是關于作業系統級別的記憶體消耗和開銷,這是解決問題的最簡單方法。
uj5u.com熱心網友回復:
我不知道 的確切實作ForEachAsync,但我假設他們使用Tasks,而不是Threads。
當您使用 1000 Tasks 運行 1000 個 CPU 系結操作時,您實際上并沒有創建 1000 Threads,您只是要求少數ThreadPool Threads 來運行這些操作。這些Threads 被Sleep呼叫阻塞,因此大多數Tasks 在開始執行之前就已排隊。
這正是為什么Thread.Sleep在 aTask或一般的異步背景關系中呼叫是一個可怕的想法的原因。如果您將代碼編輯為異步等待而不是同步等待,則經過的時間可能更接近一秒。
await Parallel.ForEachAsync(new int[1000], new ParallelOptions { MaxDegreeOfParallelism = 1000 }, async (i, token) =>
{
await Task.Delay(1000);
});
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/485010.html
