1. 程式人生 > 其它 >std::unique_lock與std::lock_guard

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 associatedTimedLockablemutex, returns if the mutex has been unavailable for the specified time duration
(public member function)
try_lock_until tries to lock the associatedTimedLockablemutex, 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