1. 程式人生 > >CPU-bound(計算密集型) 和 I/O bound(I/O密集型)

CPU-bound(計算密集型) 和 I/O bound(I/O密集型)

如果所有的任務都是計算(CPU)密集型的,則建立處理器可用核心數這麼多個執行緒就可以了,這樣已經充分利用了處理器,也就是讓它以最大火力不停進行計算。建立更多的執行緒對於程式效能反而是不利的,因為多個執行緒間頻繁進行上下文切換對於程式效能損耗較大。執行緒數=cpu核心數+1

    但如果任務都是IO密集型的,那我們就需要建立比處理器核心數大幾倍數量的執行緒。為何?當一個任務執行IO操作時,執行緒將被阻塞,於是處理器可以立即進行上下文切換以便處理其他就緒執行緒。如果我們只有處理器核心數那麼多個執行緒的話,即使有待執行的任務也無法排程處理了。執行緒數=cpu核心數/(1-阻塞係數)。一般這時候的執行緒數是cpu核心數的好幾倍。以便於執行緒發生I/O阻塞時cpu有可以進行執行緒上下文切換的執行緒。

如何設定執行緒數大小

要想合理的配置執行緒池,就必須首先分析任務特性,可以從以下幾個角度來進行分析:

  1. 任務的性質:CPU密集型任務,IO密集型任務和混合型任務。

    (1)CPU密集型任務配置儘可能小的執行緒,如配置Ncpu+1個執行緒的執行緒池。

    (2)IO密集型任務則由於執行緒並不是一直在執行任務,則配置儘可能多的執行緒,如2*Ncpu。

    (3)混合型的任務,如果可以拆分,則將其拆分成一個CPU密集型任務和一個IO密集型任務,只要這兩個任務執行的時間相差不是太大,那麼分解後執行的吞吐率要高於序列執行的吞吐率,如果這兩個任務執行時間相差太大,則沒必要進行分解。我們可以通過Runtime.getRuntime().availableProcessors()方法獲得當前裝置的CPU個數。

  2. 任務的優先順序:高,中和低。

    優先順序不同的任務可以使用優先順序佇列PriorityBlockingQueue來處理。它可以讓優先順序高的任務先得到執行,需要注意的是如果一直有優先順序高的任務提交到佇列裡,那麼優先順序低的任務可能永遠不能執行。

  3. 任務的執行時間:長,中和短。 執行時間不同的任務可以交給不同規模的執行緒池來處理,或者也可以使用優先順序佇列,讓執行時間短的任務先執行。
  4. 任務的依賴性:是否依賴其他系統資源,如資料庫連線。 依賴資料庫連線池的任務,因為執行緒提交SQL後需要等待資料庫返回結果,如果等待的時間越長CPU空閒時間就越長,那麼執行緒數應該設定越大,這樣才能更好的利用CPU。

補充:調節執行緒池大小的另一種方法如下

給定以下定義:

Ncpu:CPU的數量(Runtime.getRuntime().availableProcessors()方法獲得當前裝置的CPU個數)

Ucpu:target CPU utilization ,1 <= Ucpu <= 1

W/C:W為任務的等待時間,C為任務的執行時間

要使處理器達到期望的使用率,執行緒池的最優大小為:Ncpu * Ucpu * (1  + W/C)

IO密集型應用 - 阻塞係數大,建議多一點的執行緒數 - 建議為2倍CPU數,即假設等待時間和執行緒CPU時間一樣,阻塞係數為0.5 

CPU計算密集型應用 - 執行緒等待時間少,建議少一點的執行緒數 - 一般建議為CPU數,即假設執行緒等待時間為0,阻塞係數為0 

最佳執行緒數 cpu數/1-阻塞係數

阻塞係數 執行緒等待時間/ 執行緒等待時間 + 執行緒CPU時間