std::unique_lock與std::lock_guard
一、介紹
lock_guard和unique_lock都是RAII機制下的鎖,即依靠物件的建立和銷燬也就是其生命週期來自動實現一些邏輯,而這兩個物件就是在建立時自動加鎖,在銷燬時自動解鎖。所以如果僅僅是依靠物件生命週期實現加解鎖的話,兩者是相同的,都可以用,因跟生命週期有關,所以有時會用花括號指定其生命週期。但lock_guard的功能僅限於此。unique_lock是對lock_guard的擴充套件,允許在生命週期內再呼叫lock和unlock來加解鎖以切換鎖的狀態。二、區別
c++11中有兩個區域鎖:lock_guard和unique_lock。
- 區域鎖lock_guard使用起來比較簡單,除了建構函式外沒有其他member function,在整個區域都有效。
- 區域鎖unique_lock除了lock_guard的功能外,提供了更多的member_function,相對來說更靈活一些;但是在unique_lock佔用地記憶體更多,效率也有所下降。
三、unique_lock和lock_guard
3.1 宣告
unique_lock相交於lock_guard更加靈活,可以手動進行解鎖,但是在日常程式設計中,還是以lock_guard為主。但是標準庫也提供了第二引數的建構函式。例如:
1 explicit lock_guard (mutex_type& m);
2 lock_guard (mutex_type& m, adopt_lock_t tag);
3
4 explicit unique_lock (mutex_type& m);
5 unique_lock (mutex_type& m, try_to_lock_t tag);
6 unique_lock (mutex_type& m, defer_lock_t tag) noexcept;
7 unique_lock (mutex_type& m, adopt_lock_t tag);
8 ... ...
3.1 unique_lock部分成員函式
unique_lock的最有用的一組函式為:
lock | locks the associated mutex (public member function) |
try_lock | tries to lock the associated mutex, returns if the mutex is not available (public member function) |
try_lock_for | attempts to lock the associatedTimedLockable mutex, returns if the mutex has been unavailable for the specified time duration(public member function) |
try_lock_until | tries to lock the associatedTimedLockable mutex, returns if the mutex has been unavailable until specified time point has been reached(public member function) |
unlock | unlocks the associated mutex |
- 通過上面的函式,可以通過lock/unlock可以比較靈活的控制鎖的範圍,減小鎖的粒度。
- 通過try_lock_for/try_lock_until則可以控制加鎖的等待時間,此時這種鎖為樂觀鎖。
3.2 unique_lock取代lock_guard
unique_lock是個類模板,工作中,一般lock_guard(推薦使用);
lock_guard取代了mutex的lock()和unlock()。
unique_lock比lock_guard靈活很多靈活很多;效率上差一點,記憶體佔用多一點。
使用時std::lock_guard<std::mutex> lk(mtx);直接替換成std::unique_lock<std::mutex> lk(mtx);
3.3lock_guard和unique_lock第二引數的作用
3.3.1std::adopt_lock
std::adopt_lock標記的效果就是假設呼叫一方已經擁有了互斥量的所有權(已經lock成功了);通知lock_guard不需要再建構函式中lock這個互斥量了。
1 std::lock_guard<std::mutex> sbguard(my_mutex,std::adopt_lock);
對於lock_guard第二引數型別只有一種,鎖管理器構造的時候不會自動對可鎖物件上鎖;由可鎖物件自己加鎖;等鎖管理器析構的時候自動解鎖。
兩種使用方法
1 {
2 std::lock_guard<std::mutex> lock(g_mtx, std::adopt_lock);
3 g_mtx.lock();
4 臨界區或臨界資源
5 }
或者
1 {
2 g_mtx.lock();
3 std::lock_guard<std::mutex> lock(g_mtx, std::adopt_lock);
4 臨界區或臨界資源
5 }
如果我們指定了第二引數,但是沒有lock,鎖管理器析構的時候解鎖了無擁有權的可鎖物件,導致異常。
為什麼需要使用第二引數建構函式;直接在鎖管理器構造的時候自動加鎖不好嗎?其實官方提供第二引數建構函式是有其他作用的。比如多鎖場景下,我們會呼叫std::lock避免死鎖的出現,但是這個方法要求鎖管理器不能擁有可鎖物件,由std::lock方法執行鎖操作。如果沒有提供第二引數建構函式,那麼我們就無法使用該方法了。
std::adopt_lock和lock_guard第二引數作用類似。鎖管理器假設擁有可鎖物件,在鎖管理器析構的時候自動解鎖。注意:使用該引數型別構造的鎖管理器必須只能通過可鎖物件進行lock,不可通過鎖管理器進行lock,誤用會導致程式異常。
3.3.2std::defer_lock
用std::defer_lock的前提是,你不能自己先lock,否則會報異常,std::defer_lock的意思就是並沒有給mutex加鎖:初始化了一個沒有加鎖的mutex。
1 {
2 std::unique_lock<std::mutex> lock(g_mtx, std::defer_lock);
3 lock.lock(); // 不能用g_mtx.lock(),第二次鎖的時候會崩潰
4 臨界區或臨界資源
5 }
std::defer_lock引數作用:鎖管理器在構造的時候不主動lock且不擁有可鎖物件;如果後續執行lock,鎖管理器析構的時候自動解鎖。注意:該型別構造的鎖管理器只能通過鎖管理器執行lock且擁有可鎖物件。如果直接呼叫可鎖物件進行鎖操作後,會導致程式異常;詳情可參考原始碼。
3.3.2std::try_to_lock
1 {
2 std::unique_lock<std::mutex> lock(g_mtx, std::try_to_lock);
3 if (lock.owns_lock()) {
4 臨界區或臨界資源
5 }
6 }
std::try_to_lock引數作用:鎖管理器在構造的時候嘗試lock;如果lock上,鎖管理器就擁有可鎖物件(持有鎖),析構的時候自動執行解鎖;否則就不持可鎖物件,析構的時候也就不會解鎖;它的好處是當某個執行緒嘗試獲取該該鎖,但是該鎖已經已被其他執行緒持有,那麼不會出現執行緒被阻塞掛起。
四、參考文章
https://www.cnblogs.com/smartNeo/p/14624156.html
https://blog.csdn.net/u012507022/article/details/85909567?utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EsearchFromBaidu%7Edefault-1.pc_relevant_baidujshouduan&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EsearchFromBaidu%7Edefault-1.pc_relevant_baidujshouduan
本文來自部落格園,作者:Mr-xxx,轉載請註明原文連結:https://www.cnblogs.com/MrLiuZF/p/15081201.html