C++多線程之unique_lock的使用詳解
一、為什么會有unique_lock?
因?yàn)閙utex再管理方面有瑕疵,因此出現(xiàn)了一個(gè)互斥量封裝器lock_guard來智能的管理mutex。但是lock_guard只是簡單的管理,功能比較弱,有瑕疵。因此需要搞出來一個(gè)功能更強(qiáng)大的東西出來,而這個(gè)東西就是unique_lock。
本質(zhì)上來說lock_guard和unique_lock都是為了更好地使用各種鎖而誕生的,但是unique_lock更為靈活,功能更強(qiáng)大,可做的操作比較多。
unique_lock 有些時(shí)候也被稱為“靈活鎖”
二、std::lock_guard可能存在的問題
序號 | 問題描述 | 細(xì)節(jié) |
---|---|---|
1 | 鎖的粒度問題 | std::lock_guard的鎖作用域與對象的作用域相同,可能導(dǎo)致鎖的粒度過大,影響并發(fā)性能。 |
2 | 無法中途解鎖 | 一旦std::lock_guard對象構(gòu)造,鎖將被自動獲取,并在對象析構(gòu)時(shí)自動釋放,無法中途手動解鎖 |
3 | 不支持條件變量 | std::lock_guard通常與互斥鎖一起使用,但不支持與條件變量一起使用(如std::condition_variable)配合使用。 |
4 | 不支持遞歸鎖 | 雖然可以與std::recursive_mutex一起使用,但std::lock_guard本身不提供對遞歸鎖的特殊支持 |
5 | 無法指定鎖的嘗試獲取 | std::lock_guard總是嘗試獲取鎖,不支持非阻塞(嘗試)獲取鎖 |
6 | 無法與超時(shí)機(jī)制結(jié)合 | std::guard不提供超時(shí)機(jī)制,無法設(shè)置獲取鎖的等待時(shí)間 |
三、什么是unique_lock?
unique_lock是一個(gè)更靈活的互斥量封裝器,它提供了更多的控制選項(xiàng),比如延遲鎖定、嘗試鎖、遞歸鎖定、定時(shí)鎖定等。于std::lock_guard相比,std::unique_lock提供了更多的功能,但也需要更多的管理責(zé)任。
如何定義使用unique_lock?
unique_lock的構(gòu)造
std::mutex mtx; std::unique_lock <std::mutex> lck1(mtx);//自動上鎖 //自動鎖定 std::unique_lock <std::mutex> lck2(mtx, std::defer_lock);//延遲鎖定 std::unique_lock <std::mutex> lck3(mtx, std::adopt_lock);//接受已鎖定的mutex,不允許再次鎖定 std::unique_lock <std::mutex> lck4(mtx, std::try_to_lock);//嘗試鎖定
std::defer_lock的具體含義是告訴std::unique_lock在構(gòu)造時(shí)不要自動鎖定互斥鎖,而是延遲鎖定操作
std::unique_lock提供了一些成員函數(shù),用于管理鎖定狀態(tài)
lock()鎖定關(guān)聯(lián)的mutex
unlock()解鎖關(guān)聯(lián)的mutex
try_lock()嘗試鎖定mutex,如果鎖定成功,返回true,否則返回false
owns_lock()返回一個(gè)布爾值,指示unique_lock是否擁有mutex的所有權(quán)
unique_lock的第二個(gè)參數(shù)
std::unique_lock的構(gòu)造函數(shù)可以接受多個(gè)參數(shù),其中第二個(gè)參數(shù)用于指定如何管理鎖的行為。常見的第二個(gè)參數(shù)有以下幾種:
- std::defer_lock
- std::try_to_lock
- std::adopt_lock
- 超時(shí)相關(guān)參數(shù)(如std::chrono的時(shí)間段)
接下來我們依次用代碼例子來看
- std::defer_lock
std::defer_lock表示延遲鎖定。即在創(chuàng)建std::unique_lock對象時(shí),不會立即對互斥鎖進(jìn)行鎖定操作。
#include <iostream> #include <thread> #include <mutex> using namespace std; mutex mtx; void example_defer_lock() { unique_lock<mutex> lock(mtx,std::defer_lock);//不會立即鎖定 lock.lock();//在需要時(shí)顯式鎖定 for(int i=0;i<5;i++) { cout<<"Thread : "<<this_thread::get_id()<<" : "<<i<<endl; } cout<<"Locked with defer_lock"<<endl; lock.unlock();//顯式解鎖 } int main() { thread t1(example_defer_lock); t1.join(); return 0; }
這段代碼主要展示了如何使用 unique_lock 結(jié)合 defer_lock 策略進(jìn)行互斥鎖的操作。通過使用
defer_lock,程序可以將鎖的鎖定操作延遲到需要的時(shí)候,這在某些情況下非常有用,例如在進(jìn)行一些準(zhǔn)備工作后再進(jìn)行鎖定,或者根據(jù)條件判斷是否需要鎖定。同時(shí),使用
unique_lock 可以自動管理鎖的生命周期,避免忘記解鎖而導(dǎo)致的死鎖問題,而顯式的 lock 和 unlock
操作則提供了更靈活的控制方式,允許程序在鎖定和解鎖的時(shí)機(jī)上有更多的選擇。在 example_defer_lock 函數(shù)中,先創(chuàng)建
unique_lock 但不鎖定,之后根據(jù)需要進(jìn)行鎖定,在完成一些操作后解鎖,保證資源的正確訪問和釋放,確保線程安全。
- std::try_to_lock
std::try_to_lock表示嘗試鎖定。在創(chuàng)建std::unique_lock對象時(shí),會嘗試鎖定互斥鎖。如果鎖定失敗,不會阻塞。
#include <iostream> #include <thread> #include <mutex> using namespace std; mutex mtx; void example_try_lock() { unique_lock<mutex> lck(mtx, try_to_lock);//嘗試鎖定 if (lck.owns_lock()) { cout <<this_thread::get_id() <<" I have the lock\n" << endl; } else { cout <<""<<this_thread::get_id()<< " I don't have the lock\n" << endl; } } int main() { // 創(chuàng)建兩個(gè)線程 thread t1(example_try_lock);//線程一鎖定么互斥量 thread t2(example_try_lock);//線程二嘗試鎖定么互斥量,但是因?yàn)楸痪€程一占用著,所以沒有鎖定成功 t1.join(); t2.join(); return 0; }
這段代碼主要展示了如何使用 unique_lock 的 try_to_lock
特性來嘗試鎖定互斥鎖,并且根據(jù)鎖定結(jié)果進(jìn)行不同的處理。它創(chuàng)建了兩個(gè)線程 t1 和 t2,這兩個(gè)線程會嘗試鎖定同一個(gè)互斥鎖
mtx。由于互斥鎖的特性,只有一個(gè)線程能夠成功鎖定,另一個(gè)線程將無法鎖定。通過 owns_lock
方法可以判斷線程是否成功獲取到鎖,并輸出相應(yīng)的信息。這種方式可以避免線程阻塞,而是讓線程在無法獲取鎖時(shí)繼續(xù)執(zhí)行其他任務(wù)或采取其他處理方式。同時(shí),unique_lock
會在析構(gòu)時(shí)自動解鎖,避免了手動解鎖的繁瑣操作。
這種機(jī)制在多線程編程中非常有用,特別是當(dāng)線程需要嘗試獲取資源,但不希望在獲取不到時(shí)阻塞等待的情況,允許線程在不阻塞的情況下執(zhí)行其他任務(wù),提高程序的響應(yīng)性和資源利用率。
- std::adopt_lock
std::adopt_lock表示接管已經(jīng)鎖定的互斥鎖。此參數(shù)假設(shè)互斥鎖已經(jīng)在其他地方被鎖定,std::unique_lock對象會接管這個(gè)鎖的所有權(quán)。使用了std::adopt_lock,這意味著互斥鎖已經(jīng)被鎖定。
#include <iostream> #include <thread> #include <mutex> using namespace std; mutex mtx; void example_adopt_lock() { mtx.lock();//先鎖定互斥鎖 unique_lock<mutex> lock(mtx, adopt_lock);//接管已鎖定的鎖 cout<<"Locked with adopt_lock"<<endl; //mtx.unlock(); } int main() { thread t1(example_adopt_lock); t1.join(); return 0; }
總體來說,這段代碼的目的是展示如何在已經(jīng)手動鎖定互斥鎖的情況下,使用 std::unique_lock 結(jié)合 adopt_lock 來管理該互斥鎖,從而簡化鎖的管理并確保在適當(dāng)?shù)臅r(shí)候自動解鎖。
總結(jié)
unique_lock的特點(diǎn)
- 靈活性:unique_lock提供了更多的控制選項(xiàng),比如延遲鎖定、嘗試鎖定、遞歸鎖定、定時(shí)鎖定等。這使得開發(fā)者可以根據(jù)具體的業(yè)務(wù)邏輯來精確控制鎖的加鎖和解鎖時(shí)機(jī)。
- 可移動性:unique_lock是可移動的,可以拷貝、賦值或移動。這使得開發(fā)者可以在需要時(shí)輕松的將鎖的所有權(quán)從一個(gè)對象轉(zhuǎn)移到另一個(gè)對象。
- 手動控制:unique_lock支持手動解鎖,而lock_guard不支持。這提供了更大的靈活性,但也需要開發(fā)者自行管理鎖的加鎖和解鎖過程。
到此這篇關(guān)于C++多線程之unique_lock的使用詳解的文章就介紹到這了,更多相關(guān)C++ unique_lock內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
c實(shí)現(xiàn)linux下的數(shù)據(jù)庫備份
本文給大家簡單介紹下c實(shí)現(xiàn)linux下的數(shù)據(jù)庫備份的方法和具體的源碼,十分的實(shí)用,有需要的小伙伴可以參考下。2015-07-07C++深入講解new與deleted關(guān)鍵字的使用
這篇文章主要介紹了C++中new與deleted關(guān)鍵字的使用,new在動態(tài)內(nèi)存中為對象分配空間并返回一個(gè)指向該對象的指針;delete接受一個(gè)動態(tài)對象的指針, 銷毀該對象, 并釋放與之關(guān)聯(lián)的內(nèi)存2022-05-05