「平時注入一滴水,難時擁有太平洋。」
池化技術,通俗來講,就是提前儲存大量預分配資源,以備不時之需。執行緒池是一種多執行緒處理方式,其維護著多個執行緒,由監督者進行可併發任務的分配執行,從而避免了處理短任務時建立、銷毀程序的代價。
執行緒池的必要性
多執行緒技術主要用以解決處理器單元內多個執行緒併發執行的問題,可以顯著減少處理器單元的閒置時間,增加處理器的併發能力。執行緒的生命週期包含以下三個階段:
建立執行緒,時間開銷為t1
執行執行緒,有效執行時間為t2
銷毀執行緒時間,時間開銷為t3
即執行緒的執行效率為:η = t2 / (t1 + t2 + t3)
很明顯,如果執行緒很短,即t2在總時間中佔比比較小的話,將耗費大量額外的cpu時間和記憶體開銷,從而拉低效率η。cpu和記憶體的任務是用來處理實際的任務的,而不是頻繁的執行緒建立與銷毀。如果對於高併發服務中心,那這筆開銷將是驚人的。
為了解決這一問題,執行緒池技術也呼之欲出。
執行緒池的指導思想
執行緒池技術即致力於縮短(t1 + t3),從而提高伺服器的效能。它把t1和t3分別安排在伺服器不響應客戶端請求(即空閒時段)和伺服器程式啟動的時間段,這樣在
響應客戶端請求(即真正的工作時間t2)時,就不會有
t1和t3的開銷了。為了利用cpu多核優勢,使用多執行緒來進行任務處理,但執行緒同樣存在問題:
執行緒本身有暫存器、棧和區域性執行緒儲存的開銷
執行緒管理與上下文切換同樣存在開銷
執行緒是一種資源,可以重複使用
針對以上三個特點,尤其是第三點,並不需要把每個task請求與執行緒唯一繫結,只需要預初始化有限個執行緒,來處理不斷的task請求即可,這也就是執行緒池。
執行緒池的架構
設計時必須考慮高併發情況,即task請求數量大於預先初始化的執行緒數量,那麼對task請求必須包含三個佇列:
阻塞態工作佇列
就緒態執行緒佇列:等待處理機的到來即可開始執行
執行態執行緒佇列:正在持有多核處理機的執行緒
task任務(仿函式)被push到阻塞態工作佇列中,而就緒態執行緒佇列從阻塞態執行緒佇列內拿到task任務等待被監督者轉入執行態執行緒佇列。若獲取到任務,則將執行緒會進入執行態執行緒佇列中執行其中的仿函式。
執行緒池的實現
將上述三個佇列具現化,阻塞佇列即採用queue>來實現,表明等待被執行的仿函式佇列;就緒佇列即用vector來實現,而真正的執行佇列則是存在於cpu中的被cpu從就緒佇列中選中執行的仿函式們。threadpool.hpp
#ifndef threadpool_hpp#define threadpool_hpp#include #include #include #include #include #include namespace std;class threadpool; atomic_idlthrnum;public: inline threadpool(unsigned short size = 4) //todo inline ~threadpool() template auto commit(f &&f, args &&... args) -> future ; _tasks.emplace([task]() ); }#ifdef threadpool_auto_grow if (_idlthrnum < 1 && _pool.size() < threadpool_max_num) addthread(1);#endif _task_cv.notify_one(); return future; } int idlcount() int thrcount() #ifndef threadpool_auto_growprivate:#endif void addthread(unsigned short size); _task_cv.wait(lock, [this] ); if (!_run && _tasks.empty()) return; task = move(_tasks.front()); _tasks.pop(); } _idlthrnum--; task(); _idlthrnum++; } }); _idlthrnum++; } }};} // namespace std#endif
threadpool.cpp
語法糖與注意事項
一、注意事項首先,列印函式不應該採用std::cout,因為這個函式不是執行緒安全的函式,這會造成執行混亂,直觀的感受即多個進執行緒的輸出結果交替列印,也就像是多個印表機在同乙個頁面上打字。替代方案是採用printf()函式進行格式化輸出,這是乙個執行緒安全的函式。二、語法糖vector的emplace_back:功能與push_back無異,區別在於前者效能更高,因為push_back需要呼叫移動建構函式和建構函式(構造匿名物件),而emplace_back插入的時候直接構造,只構造一次
thread.join()是等待執行緒執行完畢
語法糖與注意事項
執行結果
github:
參考文章
[1] 執行緒池
[2]
[3]
C 語言程式設計 執行緒安全與執行緒非安全
多執行緒程式中,執行緒安全是必須要考慮的因素。執行緒安全 thread safe 就是在多執行緒環境中,多個執行緒在同一時刻對同乙份資源 e.g.暫存器 記憶體空間 全域性變數 靜態變數 etc.進行寫操作 讀操作不會涉及執行緒安全的問題 時,不會出現資料不一致。反正,則是執行緒非安全 none t...
c 單列模式與執行緒安全
通常c 裡面的單列模式很容易實現,我們也不需要去考慮其執行緒安全的問題,但是在多執行緒環境中我們卻必須要考慮到。首先我們來分析下一下的這個單列模式為什麼不是執行緒安全的,通常的單列模式寫法 class msgofarrival msgofarrival msgofarrival m pinstanc...
執行緒安全與執行緒不安全
執行緒安全是針對多執行緒來講的,如果所使用的公用變數在多執行緒下沒有被保護機制時,變數結果會和理論值不一致,這樣就叫作執行緒不安全,相反公用變數在保護機制下工作,就不會出現 隨機 變化,這時叫執行緒安全。執行緒安全 在多執行緒中使用時,不用自已做同步處理.執行緒不安全 在多執行緒中使用時,必須做執行...