前面設計同步佇列的**,下面詳細說說實現。
push
void
push
(t&& task)
; not_full_variable_.
wait
(lock,
[this])
; queue_.
push
(std::forward
(task));
not_empty_variable_.
notify_one()
;}void
push
(const t& task)
; not_full_variable_.
wait
(lock,
[this])
; queue_.
push
(std::
move
(task));
not_empty_variable_.
notify_one()
;}
為了保證執行緒安全,首先建立了乙個unique_lock獲取mutex,接著通過佇列非滿條件變數來等待佇列非滿的時候。這裡使用lambda表示式來返回佇列是否已滿,如果條件不滿足,條件變數會釋放mutex,並將執行緒置於waiting狀態,等待其他執行緒發出通知喚醒自己;如果條件滿足,則繼續往下執行,往佇列中新增任務,接著發出佇列非空的通知,喚醒乙個正在處於等待狀態的執行緒取走任務。
右值引用
這裡有兩個push函式,其實它們實現的功能是一樣的,引數為t&&task是為了實現移動語句,它其實是乙個不定的型別,可能是左值也可能是右值,如果引數是右值,就可以避免對臨時物件的深拷貝,提高效能。
std::forward
乙個右值引用引數作為函式的形參時,在這個函式內部如果再傳遞該引數給其函式,這個引數已經變成了乙個左值,不會按照我們預期的型別進行傳遞。std::forward就是為了實現需要的完美**,即保持引數的型別。不管引數t&&這種未定的引用還是明確的左值引用或者右值引用,它會按照引數本來的型別**。
std::move
可以將乙個左值強制轉換為乙個右值引用,這樣就可以通過移動構造,避免深拷貝。
pop
void
pop(t& task)
; not_empty_variable_.
wait
(lock,
[this])
; task = queue_.
front()
; queue_.
pop();
not_full_variable_.
notify_one()
;}void
pop(std::queue
& tasks)
; not_empty_variable_.
wait
(lock,
[this])
; tasks = std::
move
(queue_)
; not_full_variable_.
notify_one()
;}
這裡有兩個pop函式,功能分別為只獲取乙個任務,和獲取全部任務。如果只有獲取乙個任務介面,因為每次獲取任務都需要獲取鎖,想要獲取全部任務就需要呼叫多次,效率比較低。因此設計多設計了乙個獲取全部任務的介面,通過move的方式,將佇列的所有任務都交換出來,避免了資料的複製。
它的流程和push的過程類似,先獲取mutex,然後通過佇列非空條件變數來等待佇列非空的時候,不滿足時釋放mutex繼續等待,滿足則會將任務從佇列中取出,並喚醒乙個在等待新增任務的執行緒去新增任務。
empty full count clear
empty函式用於判斷佇列是否為空
full函式用於判斷佇列是否已滿
count函式用於獲取佇列中的等待執行的任務數量
clear函式用於清除佇列中等待的任務
鎖和條件變數
std::mutex
獨佔互斥鎖,可以通過lock()方法阻塞執行緒,直到使用unlock()來解除對互斥量的占用。
std::lock_guard
只使用std::mutex需要先呼叫lock()方法鎖,再呼叫unlock()來釋放鎖。因此,有時候就可能會出現忘記呼叫unlcok()的情況。而呼叫std::lock_guard可以簡化鎖的使用,它在構造的時候會自動鎖定互斥鎖,在作用域結束的時候自動地解鎖。
std::condition_variable
條件變數需要和互斥量配合使用,它可以阻塞乙個或者多個執行緒,直到收到其他執行緒發出的通知或者超時才會喚醒當前阻塞的執行緒。
std::unique_lock lock
;not_empty_variable_.
wait
(lock,
[this])
;
unique_lock和std::lock_guard是類似的,可以保證安全釋放mutex,區別在於std::lock_guard需要等到作用域結束才釋放mutex,而unique_lock可以自由的釋放mutex。因此可以和條件變數配置使用。
not_empty_variable_就是乙個std::condition_variable,它會先檢查佇列非空條件是否滿足,如果滿足則重新獲取mutex,結束wait繼續執行;如果不滿足則會釋放mutex,將執行緒置為waiting狀態,等待喚醒。
現在我們已經實現了同步佇列,下面我們將考慮執行緒池的設計。
c 執行緒池實現(二)同步佇列實現
前面我們介紹了半同步半非同步執行緒池的概念,下面我們首先說一下同步佇列。同步佇列是半同步半非同步執行緒池三層中的中間層 排隊層。它一方面提供介面給上面同步服務層新增新任務,一方面提供介面給下面的非同步服務層獲取任務。上層可能是併發的新增任務,因此同步佇列需要保證任務是執行緒安全的,同時它還需要確保任...
c 執行緒池實現(四)執行緒池實現
前面已經說到了同步佇列的實現,下面來看執行緒池的實現。ifndef include threadpool define include threadpool include include include include include syncqueue.hpp namespace mythrea...
c 實現執行緒池
執行緒池 簡單地說,執行緒池 就是預先建立好一批執行緒,方便 快速地處理收到的業務。比起傳統的到來乙個任務,即時建立乙個執行緒來處理,節省了執行緒的建立和 的開銷,響應更快,效率更高。在linux中,使用的是posix執行緒庫,首先介紹幾個常用的函式 1 執行緒的建立和取消函式 pthread cr...