C++並發與多線程學習筆記--unique_lock詳解


  • unique_lock 取代lock_quard
  • unique_lock 的第二個參數
    • std::adopt_lock
    • std::try_to_lock
    • std::defer_lock
  • unique_lock的成員函數
    • lock()
    • unlock()
    • try_to_lock()
    • release()
  • unique_lock所有權的傳遞

 

unique_lock 取代lock_guard

 

應用場景:兩個線程A、B,其中A對隊列添加元素,B移除元素。

unique_lock是個類模板,工作中,一般使用lock_gard(推薦使用),取代mutex的lock()和unlock()。unique_lock比lock_gard靈活許多,但是unique_lock效率差一點,並且內存占用多一點。

std::unique_lock<std::mutex> sbgard(mu_mutex);

unique_lock 的第二個參數

unique_lock比較靈活,主要是因為這個第二個參數。 lock_guard可以帶第二個參數。

std::unique_lock<std::mutex> sbguard(my_mutex, std::adopt_lock);

  std::adopt_lock這個參數表示標記作用,unique_lock支持更多參數,表示互斥量已經被lock了

std::adopt_lock:表示這個互斥量已經被Lock了,必須把互斥量提前lock,否則會報異常。這個標記的效果:假設調用方線程已經擁有了互斥的所有權,也就是說已經lock成功了,通知lock_guard不需要在構造函數中Lock這個互斥量了。unique_lock也是同樣的功能,就是不希望在unique_lock()的構造函數中再次lock了。

    std::adopt_lock

用adopt_lock的前提條件是要先給互斥量加鎖,然后在后面的語句中不會再次lock(),即后續的std::lock_guard中才能使用std::adopt_lock這個參數,然后unique_lock自動unlock。

std::unique_lock<std::mutex> sbguard(my_mutex, std::adopt_lock);

  如果另外一個線程A拿了鎖之后,執行20s,然后這個線程B等待A釋放鎖。此時,A一直占用資源,B一直得不到資源,unique_lock如果一直拿不到鎖,讓它去先做別的事情,所以引入了try_to_lock。

    std::try_to_lock

std::try_to_lcok會嘗試用 mutex的lock()去鎖定mutex,但是沒有鎖定成功,也會立即返回,並不會阻塞。用try_to_lock的前提是不能使用lock:

std::unique_lock<std::mutex> sbguard(my_mutex, std::try_to_lock);

  a)注意,不能先去try_to_lock,嘗試加鎖。此時就有成功或者失敗,嘗試拿鎖成功或者嘗試拿鎖失敗。

  b)如果拿到了鎖,才能去操作共享數據(臨界區),sbguard.owns_lock拿到鎖,就不能操作共享數據,可以做一些別的事情。

  c)A拿到鎖,sleep 20s,此時B拿不到鎖,一直在等待。線程不需要一直等資源,通過sbguard.owns_lock的值進行判斷。

    std::defer_lock

前提:不能自己先lock,否者會出現異常。defer_lock的意思不需要給mutex加鎖:初始化了沒有加鎖的mutex,沒加鎖的mutex,可以靈活地調用unique_lock的一些重要的成員函數。

std::unique_lock<std::mutex> sbguard(my_mutex, std::defer_lock);

 之后需要手動 lock,unlock在析構函數中自動執行了。

 

unique_lock的成員函數

a)lock(),加鎖

b)unlock(),解鎖

有lock就可以有unlock,不過在unique_lock中使用了鎖,類的析構函數執行了解鎖的操作。有的時候會出問題,但是實際上不穩定。有的時候可能臨時需要處理一些其他事情。

lock()

//處理一些共享數據

unlock()

  也可以用unqiue_lock,編碼變得更加靈活:

std::unique_lock<std::mutex> sbguard(mutex);

lock();

//處理一些共享數據

unlock();

//處理一些非共享數據

lock();

////

////

  此時unlock不必手工unlock,此時的unlock在unique_lock的析構函數中執行。

    try_lock()

嘗試給互斥量加鎖,如果拿不到鎖,則返回false,如果拿到了鎖,返回true,函數不阻塞。

if(sbguard.try_lock()==true)
{
    //拿到鎖了
    訪問共享數據
}else
{
     //沒拿到鎖
     
}

    release()

返回它所管理的mutex對象指針,並釋放所有權,也就是說這個unique_lock和mutex不再有關系,嚴格區分unlock()和release()的區別。release()把綁在一起的東西解開了(解開關系),unlock()是解開鎖頭。

如果原來mutex對象處於加鎖狀態,需要接管過來並負責解鎖。

std::unique_lock<std::mutex> sbguard(my_mutex);
std::mutex *ptx = sbguard.release(); 
//此時sbguard和my_mutex的關系解除了,現在就有責任自己解鎖my_mutex
//如果自己不解鎖,那么就卡住了

解鎖語法:自己負責my_mutex的解鎖。

ptx->nulock();

 release()返回的是原始的mutex指針。

鎖頭鎖住的代碼的多少成為粒度,粒度一般用粗細來描述;

a)鎖住代碼少,粒度細,執行效率高。

b)鎖住代碼多,粒度粗,執行效率低。

要學會盡量選擇合適粒度的代碼進行保護。

unique_lock所有權的傳遞

 unique_lock需要和mutex綁定到一起才是一個完整的unique_lock,應該是unique_lock需要管理一個mutex指針才能起作用。

所有權:指定unique_lock擁有mutex的所有權,unique_lock可以把所有用的所有權,可以轉移給其他的unique_lock對象,所有權轉移,mutex可以轉移,但是不能復制

可以使用std::move(mutex)

std::unique_lock<std::mutex>  sbguard1(mutex);
std::unique_lock<std::mutex>  sbguard2(std::move(mutex)); 

 此時sbguard1為空,sbguard2接管了原來sbguard1和mutex的關系,其實就是新的unique_lock()指向了原來的mutex。

參考文獻

https://blog.csdn.net/guanguanboy/article/details/100514731

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM