高併發下,怎麼選擇最優的線程數?

最常見的說法應該是,採用線程池,他可以有效降低線程創建釋放的時間花銷及資源開銷,如不使用線程池,有可能造成系統創建大量線程而導致消耗完系統內存以及“過度切換”(在JVM中採用的處理機制爲時間片輪轉,減少了線程間的相互切換) 。
下面是獲得cou數目的方法。

int N_CPUS = Runtime.getRuntime().availableProcessors();

最好的線程數計算方法:

Ncpu = CPU的數量
Ucpu = 目標CPU的使用率, 0 <= Ucpu <= 1,一般極端情況下設爲1
W/C = 等待時間與計算時間的比率
爲保持處理器達到期望的使用率,最優的池的大小等於:
Nthreads = Ncpu x Ucpu x (1 + W/C)

爲了正確地設置線程池的長度,你必須估算出任務花在等待的時間與用來計算的時間的比率(cpu利用率);這個估算值不必十分精確,而且可以通過一些監控工具獲得。在控制變量法的作用下,使用visualvm一般可以做到估算。(這是對於包含了 I/O和其他阻塞操作的任務,不是所有的線程都會在所有的時間被調度,因此你需要一個更大的池。)
不過,對於計算密集型的任務,一個有Ncpu個處理器的系統通常通過使用一個Ncpu + 1個線程的線程池來獲得最優的利用率(計算密集型的線程恰好在某時因爲發生一個頁錯誤或者因其他原因而暫停,剛好**有一個“額外”的線程,**可以確保在這種情況下CPU週期不會中斷工作)。

Nthreads = Ncpu x (1 + W/C)
即線程等待時間所佔比例越高,需要越多線程。線程CPU計算時間所佔比例越高,需要越少線程。
這就可以劃分成兩種任務類型:
IO密集型 一般情況下,如果存在IO,那麼肯定W/C > 1(阻塞耗時一般都是計算耗時的很多倍),但是需要考慮系統內存有限(每開啓一個線程都需要內存空間),這裏需要在服務器上測試具體多少個線程數適合(CPU佔比、線程數、總耗時、內存消耗)。如果不想去測試,保守點取1即可,Nthreads = Ncpu x (1 + 1) = 2Ncpu。這樣設置一般都OK。
計算密集型 假設沒有等待W = 0,則W/C = 0。Nthreads = Ncpu。再加個1。
根據短板效應,真實的系統吞吐量並不能單純根據CPU來計算。那要提高系統吞吐量,就需要從“系統短板”(比如網絡延遲、IO)着手:
儘量提高短板操作的並行化比率,比如多線程下載技術;
增強短板能力,比如用NIO替代IO;
第一條可以聯繫到Amdahl定律,正好計算機體系結構上過這門課,這條定律定義了串行系統並行化後的加速比計算公式:加速比 = 優化前系統耗時 / 優化後系統耗時 加速比越大,表明系統並行化的優化效果越好。Addahl定律還給出了系統並行度、CPU數目和加速比的關係,加速比爲Speedup,系統串行化比率(指串行執行代碼所佔比率)爲F,CPU數目爲N:Speedup <= 1 / (F + (1-F)/N)
當N足夠大時,串行化比率F越小,加速比Speedup越大。
我想最終結論最好爲:
IO密集型 = 2Ncpu(可以測試後自己控制大小,2Ncpu一般沒問題)(常出現於線程中:數據庫數據交互、文件上傳下載、網絡數據傳輸等等)
計算密集型 = Ncpu+1

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章