你會在這裏遇到幾個問題。調度程序的飢餓迴避機制會將您的任務視爲等待進程時被阻止。它會發現很難區分死鎖線程和簡單等待進程完成的線程。因此,如果您的任務運行或很長時間(見下文),它可能會安排新的任務。登山啓發式應考慮系統的整體負載,無論是從應用程序還是其他應用程序。它只是試圖最大限度地完成工作,所以它會增加更多的工作,直到系統的整體吞吐量停止增加,然後退出。我不認爲這將影響您的應用程序,但避免問題的可能會。
你可以找到更多的細節,如何這在Parallel Programming with Microsoft®.NET,科林坎貝爾,拉爾夫·約翰遜,米勒阿德,斯蒂芬Toub(是online早期草案)所有的作品。
「的.NET線程池自動根據管理工作者在游泳池 線程數它添加和刪除線程內置 啓發式的.NET線程池具有注入 線程兩種主要機制:A飢餓避免機制,增加了工人 線程,如果它看到在排隊的物品,並試圖在使用作爲 幾個線程儘可能地最大化吞吐量爬山 啓發而毫無進展。
飢餓避稅的目的是爲了防止死鎖這種類型的死鎖可能發生在工作人員th讀取等待同步 事件,該事件只能由線程池的全局或本地隊列中仍處於待執行 的工作項目滿足。如果有一個固定的 工作者線程數,並且所有這些線程同樣被阻止,則系統將無法取得進一步的進展。 添加新的工作線程可解決問題。
爬山啓發式算法的一個目標是在線程被I/O或其他等待條件阻塞的等待條件 阻止處理器時提高內核的利用率 。默認情況下,託管線程池每個核心有一個 工作線程。如果其中一個工作線程變爲 被阻止,則核心可能未充分利用,這取決於計算機整體工作負載上的 。線程注入邏輯 不區分被阻塞的線程和正在執行冗長的處理器密集型操作的線程 。因此,每當線程池的全局或本地隊列包含待定的 工作項時,需要花費很長時間運行的活動工作項(大於 半秒)纔會觸發新線程池工作線程的創建 線程。
.NET線程池有機會每工作項完成 時間或以500毫秒間隔注入線程,無論哪個 更短。線程池使用此機會嘗試添加線程 (或將它們帶走),並根據線程數的以前更改的反饋進行指導。如果添加線程似乎有助於吞吐量,則線程池會增加更多;否則,它會減少工作線程的數量。這種技術被稱爲爬山啓發式。 因此,保持單個任務簡短的一個原因是爲了避免 「飢餓檢測」,但是另一個保持簡短的原因是 給線程池更多的機會來提高吞吐量,調整線程數量爲 。單個任務的持續時間越短,線程池可以測量吞吐量的頻率越高,並且相應地調整線程計數。
爲了使這個具體,請考慮一個極端的例子。假設你有一個複雜的財務模擬和 操作,其中每個操作需要花費10分鐘的時間才能完成,需要500個處理器密集的 操作。如果您在全局隊列中爲這些操作的每個 創建頂級任務,則會發現大約五分鐘後,線程池將增長到500個工作線程。原因在於 線程池將所有任務視爲被阻止,並開始以每秒大約兩個線程的速率添加新的線程。
500工作線程有什麼問題?原則上,沒有任何內容,如果你有500個內核供他們使用,並且海量內存系統爲 。事實上,這是並行計算的長期願景。 但是,如果您的計算機上沒有多個內核,則在多個線程正在競爭時間片的情況下,您的系統爲 。這種情況被稱爲處理器超額訂購。允許許多處理器密集型線程在單個內核上競爭時間會增加上下文切換開銷,這會嚴重降低整個系統的吞吐量。即使你沒有耗盡內存,在這個 的情況下的性能可能會比連續計算更糟糕得多。 (每個上下文切換需要6,000到8,000個處理器週期。) 上下文切換的開銷不是唯一的開銷來源。 .NET中的託管線程佔用大約1兆字節的堆棧空間,無論該空間是否用於當前正在執行的功能。 需要大約200,000個CPU週期來創建一個新線程,並且大約需要100,000個週期來退出一個線程。這些是昂貴的操作。
只要你的任務不需要每個分鐘,線程池的登山算法就會最終意識到它有太多線程 並自行減少。但是,如果你的任務 佔用一個工作線程幾秒或幾分鐘或幾小時,那麼 將拋出線程池的啓發式,此時 應考慮替代方案。
第一個選項是將您的應用程序分解爲更短的 任務,這些任務的完成速度足以使線程池成功執行 控制線程數以獲得最佳吞吐量。 第二種可能性是實現您自己的任務調度程序 不執行線程注入的對象。如果您的任務持續時間很長,則不需要高度優化的任務調度程序,因爲與任務的執行時間 相比,調度的成本可以忽略不計。 MSDN®開發人員計劃有一個 簡單任務調度程序實現的示例,該實現限制併發的最大度數 。有關更多信息,請參閱本章末尾的「進一步閱讀」部分, 。
作爲最後的手段,您可以使用SetMaxThreads方法 配置ThreadPool類有上限的工作線程的數量 ,通常等於(這是 Environment.ProcessorCount屬性)核心數量。此上限適用於 的整個過程,包括所有的應用程序域「
當然,我應該做的是將一個TaskCompletionSource連接到Process.Exited事件,然後有一個返回Task的TranscodeAsync方法。它會是非阻塞的。然後,我可以對任務進行更細緻的控制,同時仍然呆在第三方物流的糧食中。 – 2012-02-03 14:19:17