1. 程式人生 > 程式設計 >深入理解-CPU核心數與執行緒池併發執行緒數關係

深入理解-CPU核心數與執行緒池併發執行緒數關係

那是一個風和日麗的下午!

面試官微微一笑,對我說:“小夥子,合理配置執行緒池你是如何考慮的?

我微微一笑,說出了我的答案:

首先確認業務是CPU密集型還是IO密集型的,

如果是CPU密集型的,那麼就應該儘量少的執行緒數量,一般為CPU的核數+1;

如果是IO密集型:所以可多分配一點 cpu核數*2 也可以使用公式:CPU 核數 / (1 - 阻塞係數);其中阻塞係數在 0.8 ~ 0.9 之間。

面試官心想,有點東西,接著追問:“那你是如何得到的這個公式,或者說你是如何根據CPU核心數確定執行緒池併發執行緒數的

我心想這不是小瞧我嘛?不屑回答的我脫下了我的帽子

哈哈哈哈,進入正題!


1,追書溯源

關於如何計算併發的執行緒數量,一般分兩派,來自兩本書,且都是好書,

第一派:《Java Concurrency in Practice》即《java併發程式設計實踐》,我們簡稱A派。

第二派:《Programming Concurrency on the JVM Mastering》即《Java 虛擬機器器併發程式設計》,我們簡稱B派。

到底哪個是對的?我們擷取書中部分內容具體看看。

A派一書中是這樣寫的

B派一書中是這樣寫的

2,大膽分析

對於A派,假設CPU跑滿,即撇開CPU使用率這個因素,A派認為 執行緒數 = Ncpu * ( 1 + w/c )

大膽假設將B派的公式等於A派公式,

Ncpu / (1-阻塞係數) = Ncpu * ( 1 + w/c )阻塞係數 = 阻塞時間 /(阻塞時間+計算時間)

這個結論在派系二後續中得到應徵,如下圖:

\color{red}{所以得出一個結論:A派和B派其實是一個公式!}

3,實際運用

那麼實際使用中併發執行緒數如何設定呢?分析如下(我們以A派公式為例):

Nthreads = Ncpu * (1+w/c)

IO密集型:一般情況下,如果存在IO,那麼肯定w/c>1(阻塞耗時一般都是計算耗時的很多倍),但是需要考慮系統記憶體有限(每開啟一個執行緒都需要記憶體空間),這裡需要上伺服器測試具體多少個執行緒數適合(CPU佔比、執行緒數、總耗時、記憶體消耗)。

如果不想去測試,保守點取1即,Nthreads=Ncpu*(1+1)=2Ncpu。這樣設定一般都OK。

CPU密集型: 假設沒有等待w=0,則W/C=0. Nthreads=Ncpu。

再次歸納下:

IO密集型 = 2Ncpu(可以測試後自己控制大小,2Ncpu一般沒問題)(常出現於執行緒中:資料庫資料互動、檔案上傳下載、網路資料傳輸等等)

CPU密集型 = Ncpu(常出現於執行緒中:複雜演演算法)

java中:Ncpu = Runtime.getRuntime().availableProcessors()

4,繼續研究

A派的另一個說法

即對於計算密集型的任務,在擁有N個處理器的系統上,當執行緒池的大小為N+1時,通常能實現最優的效率。(即使當計算密集型的執行緒偶爾由於缺失故障或者其他原因而暫停時,這個額外的執行緒也能確保CPU的時鐘週期不會被浪費。)

即,計算密集型=Ncpu+1,但是這種做法導致的多一個cpu上下文切換是否值得,這裡不考慮。讀者可自己考量。


本文借鑑歸納:Flag Counter 《根據CPU核心數確定執行緒池併發執行緒數》