1. 程式人生 > 實用技巧 >C++11多執行緒程式設計(三)——lock_guard和unique_lock

C++11多執行緒程式設計(三)——lock_guard和unique_lock

如果熟悉C++多執行緒的童鞋可能有了解到實現的互斥鎖的機制還有這個寫法

lock_guard<mutex> guard(mt);

那麼這句話是什麼意思呢?為什麼又要搞個這樣的寫法呢?

這個也是構造互斥鎖的寫法,就是會在lock_guard建構函式里加鎖,在解構函式裡解鎖,之所以搞了這個寫法,C++委員會的解釋是防止使用mutex加鎖解鎖的時候,忘記解鎖unlock了。

#include <iostream>
#include <thread>
#include <string>
#include <mutex>
using namespace
std; mutex mt; void thread_task() { for (int i = 0; i < 10; i++) { lock_guard<mutex> guard(mt); cout << "print thread: " << i << endl; } } int main() { thread t(thread_task); for (int i = 0; i > -10; i--) { lock_guard<mutex> guard(mt); cout << "print main: " << i << endl; } t.join();
return 0; }

這裡說解構函式裡解鎖,那麼到底什麼時候呼叫解構函式呢?建構函式加鎖我們好理解,寫下這個語句的時候呼叫lock_guard<mutex> guard(mt),那麼呼叫解構函式應該是大括號{}結束的時候,也就是說定義lock_guard的時候呼叫建構函式加鎖,大括號解鎖的時候呼叫解構函式解鎖。

雖然lock_guard挺好用的,但是有個很大的缺陷,在定義lock_guard的地方會呼叫建構函式加鎖,在離開定義域的話lock_guard就會被銷燬,呼叫解構函式解鎖。這就產生了一個問題,如果這個定義域範圍很大的話,那麼鎖的粒度就很大,很大程式上會影響效率。

所以為了解決lock_guard鎖的粒度過大的原因,unique_lock就出現了。

unique_lock<mutex> unique(mt);

這個會在建構函式加鎖,然後可以利用unique.unlock()來解鎖,所以當你覺得鎖的粒度太多的時候,可以利用這個來解鎖,而析構的時候會判斷當前鎖的狀態來決定是否解鎖,如果當前狀態已經是解鎖狀態了,那麼就不會再次解鎖,而如果當前狀態是加鎖狀態,就會自動呼叫unique.unlock()來解鎖。而lock_guard在析構的時候一定會解鎖,也沒有中途解鎖的功能。

當然,方便肯定是有代價的,unique_lock內部會維護一個鎖的狀態,所以在效率上肯定會比lock_guard慢。

所以,以上兩種加鎖解鎖的方法,加上前面文章介紹的mutex方法,具體該使用哪一個,要依照具體的業務需求來決定,當然mt.lock()和mt.unlock()不管是哪種情況,是肯定都可以使用的。

對我而言,總感覺這個lock_guard有點雞肋而已,完全可以用mutex來替代,忘記解鎖的話一般都可以通過除錯發現,而且一般情況下都不會忘記。僅僅只是因為怕忘記解鎖這個原因的話,真的感覺有點多此一舉,徒增學習成本罷了。

當然也許C++委員會有他們自己的考慮,對於我們而言,也只能記住就是了。

更多精彩內容,請關注同名公眾:一點筆記alittle