前言:我知道使用ThreadPool(通過TPL
或直接)用於IO操作is generally frowned upon,因爲IO必須是順序的,但是我的問題涉及阻塞調用的「並行IO」不要公開Async
方法。使用TPL與並行阻塞IO操作
我正在寫一個GUI工具,獲取有關網絡上的計算機,做這個(簡化代碼)信息:
String[] computerNames = { "foo", "bar", "baz" };
foreach(String computerName in computerNames) {
Task.Factory
.StartNew(GetComputerInfo, computerName)
.ContinueWith(ShowOutputInGui, RunOnGuiThread);
}
private ComputerInfo GetComputerInfo(String machineName) {
Task<Int64> pingTime = Task.Factory.StartNew(() => GetPingTime(machineName));
Task<Process[]> processes = Task.Factory.StartNew(() => System.Diagnostics.Process.GetProcesses(machineName));
// and loads more
Task.WaitAll(pingtime, processes, etc);
return new ComputerInfo(pingTime.Result, processes.Result, etc);
}
當我運行這段代碼我發現它需要一個令人驚訝的長量與我曾經使用過的舊順序代碼相比,它的運行時間更長
注意的是,在GetComputerInfo
方法每個任務完全獨立於其他周圍的(如平時間可分別從GetProcesses
計算),但是當我插入一些Stopwatch
定時調用,我發現,各個子任務,如在調用GetComputerInfo
之後,GetProcesses
調用僅啓動至3000ms
- 存在一些大的延遲。
我注意到,當我減少外部並行調用的數量到GetComputerInfo
(通過減小computerNames
數組的大小),第一個結果幾乎立即返回。一些計算機名稱是關閉的計算機,所以稱爲GetProcesses
和PingTime
需要很長時間才能超時(我的真實代碼捕捉到了例外)。這可能是因爲離線計算機阻塞Tasks
正在運行,並且TPL自然將其限制爲我的CPU硬件線程數(8)。
有沒有辦法告訴TPL不要讓內部任務(例如GetProcesses
)阻止外部任務(GetComputerInfo
)?我試過「Parent/Child」任務附件/阻塞,但它不適用於我的情況,因爲我從不明確地將子任務附加到父任務,父任務自然會等待Task.WaitAll
) 。
也許會更好,如果'GetComputerInfo( )'沒有'Task.WaitAll()'在其中....有點失敗的目的。爲什麼不返回'任務[]'? – MickyD
沒有一個好的[mcve]可靠地重現問題,如果不是不可能診斷問題是困難的。也就是說,請記住,線程池沒有無限數量的線程在等着你。在空閒狀態下,它將只有少數線程(最多等於CPU內核數量),並且每半秒鐘只啓動一次新線程(這是可配置的IIRC,我不記得默認情況)。您可以通過以下方式來幫助:a)使用異步ping方法,並且b)使用ThreadPool.SetMinThreads()來增加空閒時的線程數量,以便在需要時準備好它們。 –
請注意,調用'SetMinThreads()'有點破解。很不幸,.NET沒有'GetProcesses()'方法的異步版本;但我甚至沒有看到用本機代碼實現的異步方式。如果你真的希望這些操作能夠平行進行,我認爲你自己就不能自己管理這些線程。 –