1. 程式人生 > >執行緒數究竟設多少合理

執行緒數究竟設多少合理

一、需求緣起

      Web-Server通常有個配置,最大工作執行緒數,後端服務一般也有個配置,工作執行緒池的執行緒數量,這個執行緒數的配置不同的業務架構師有不同的經驗值,有些業務設定為CPU核數的2倍,有些業務設定為CPU核數的8倍,有些業務設定為CPU核數的32倍。

“工作執行緒數”的設定依據是什麼,到底設定為多少能夠最大化CPU效能,是本文要討論的問題。

二、一些共性認知

在進行進一步深入討論之前,先以提問的方式就一些共性認知達成一致。

提問:工作執行緒數是不是設定的越大越好?

回答:肯定不是的

1)一來伺服器CPU核數有限,同時併發的執行緒數是有限的,1核CPU設定10000個工作執行緒沒有意義

2)執行緒切換是有開銷的,如果執行緒切換過於頻繁,反而會使效能降低

提問:呼叫sleep()函式的時候,執行緒是否一直佔用CPU

回答:不佔用,等待時會把CPU讓出來,給其他需要CPU資源的執行緒使用

不止呼叫sleep()函式,在進行一些阻塞呼叫,例如網路程式設計中的阻塞accept()【等待客戶端連線】和阻塞recv()【等待下游回包】也不佔用CPU資源

提問:如果CPU是單核,設定多執行緒有意義麼,能提高併發效能麼?

回答:即使是單核,使用多執行緒也是有意義的

1)多執行緒編碼可以讓我們的服務/程式碼更加清晰,有些IO執行緒收發包,有些Worker執行緒進行任務處理,有些Timeout執行緒進行超時檢測

2)如果有一個任務一直佔用CPU資源在進行計算,那麼此時增加執行緒並不能增加併發,例如這樣的一個程式碼

 while(1){ i++; }

該程式碼一直不停的佔用CPU資源進行計算,會使CPU佔用率達到100%

3)通常來說,Worker執行緒一般不會一直佔用CPU進行計算,此時即使CPU是單核,增加Worker執行緒也能夠提高併發,因為這個執行緒在休息的時候,其他的執行緒可以繼續工作

三、常見服務執行緒模型

瞭解常見的服務執行緒模型,有助於理解服務併發的原理,一般來說網際網路常見的服務執行緒模型有如下兩種

IO執行緒與工作執行緒通過佇列解耦類模型

如上圖,大部分Web-Server與服務框架都是使用這樣的一種“IO執行緒與Worker執行緒通過佇列解耦”類執行緒模型:

1)有少數幾個IO執行緒監聽上游發過來的請求,並進行收發包(生產者)

2)有一個或者多個任務佇列,作為IO執行緒與Worker執行緒非同步解耦的資料傳輸通道(臨界資源)

3)有多個工作執行緒執行正真的任務(消費者)

這個執行緒模型應用很廣,符合大部分場景,這個執行緒模型的特點是,工作執行緒內部是同步阻塞執行任務的(回想一下tomcat執行緒中是怎麼執行Java程式的,dubbo工作執行緒中是怎麼執行任務的),因此可以通過增加Worker執行緒數來增加併發能力,今天要討論的重點是“該模型Worker執行緒數設定為多少能達到最大的併發”。

純非同步執行緒模型

任何地方都沒有阻塞,這種執行緒模型只需要設定很少的執行緒數就能夠做到很高的吞吐量,Lighttpd有一種單程序單執行緒模式,併發處理能力很強,就是使用的的這種模型。該模型的缺點是:

1)如果使用單執行緒模式,難以利用多CPU多核的優勢

2)程式設計師更習慣寫同步程式碼,callback的方式對程式碼的可讀性有衝擊,對程式設計師的要求也更高

3)框架更復雜,往往需要server端收發元件,server端佇列,client端收發元件,client端佇列,上下文管理元件,有限狀態機元件,超時管理元件的支援

however,這個模型不是今天討論的重點。

四、工作執行緒的工作模式

瞭解工作執行緒的工作模式,對量化分析執行緒數的設定非常有幫助:

上圖是一個典型的工作執行緒的處理過程,從開始處理start到結束處理end,該任務的處理共有7個步驟:

1)從工作佇列裡拿出任務,進行一些本地初始化計算,例如http協議分析、引數解析、引數校驗等

2)訪問cache拿一些資料

3)拿到cache裡的資料後,再進行一些本地計算,這些計算和業務邏輯相關

4)通過RPC呼叫下游service再拿一些資料,或者讓下游service去處理一些相關的任務

5)RPC呼叫結束後,再進行一些本地計算,怎麼計算和業務邏輯相關

6)訪問DB進行一些資料操作

7)操作完資料庫之後做一些收尾工作,同樣這些收尾工作也是本地計算,和業務邏輯相關

分析整個處理的時間軸,會發現:

1)其中1,3,5,7步驟中【上圖中粉色時間軸】,執行緒進行本地業務邏輯計算時需要佔用CPU

2)而2,4,6步驟中【上圖中橙色時間軸】,訪問cache、service、DB過程中執行緒處於一個等待結果的狀態,不需要佔用CPU,進一步的分解,這個“等待結果”的時間共分為三部分:

2.1)請求在網路上傳輸到下游的cache、service、DB

2.2)下游cache、service、DB進行任務處理

2.3)cache、service、DB將報文在網路上傳回工作執行緒

五、量化分析併合理設定工作執行緒數

最後一起來回答工作執行緒數設定為多少合理的問題。

通過上面的分析,Worker執行緒在執行的過程中,有一部計算時間需要佔用CPU,另一部分等待時間不需要佔用CPU,通過量化分析,例如打日誌進行統計,可以統計出整個Worker執行緒執行過程中這兩部分時間的比例,例如:

1)時間軸1,3,5,7【上圖中粉色時間軸】的計算執行時間是100ms

2)時間軸2,4,6【上圖中橙色時間軸】的等待時間也是100ms

得到的結果是,這個執行緒計算和等待的時間是1:1,即有50%的時間在計算(佔用CPU),50%的時間在等待(不佔用CPU):

1)假設此時是單核,則設定為2個工作執行緒就可以把CPU充分利用起來,讓CPU跑到100%

2)假設此時是N核,則設定為2N個工作現場就可以把CPU充分利用起來,讓CPU跑到N*100%

結論:

N核伺服器,通過執行業務的單執行緒分析出本地計算時間為x,等待時間為y,則工作執行緒數(執行緒池執行緒數)設定為 N*(x+y)/x,能讓CPU的利用率最大化。

經驗:

    一般來說,非CPU密集型的業務(加解密、壓縮解壓縮、搜尋排序等業務是CPU密集型的業務),瓶頸都在後端資料庫,本地CPU計算的時間很少,所以設定幾十或者幾百個工作執行緒也都是可能的。