- 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