重慶分公司,新征程啟航
為企業(yè)提供網(wǎng)站建設(shè)、域名注冊、服務(wù)器等服務(wù)
為企業(yè)提供網(wǎng)站建設(shè)、域名注冊、服務(wù)器等服務(wù)
今天就跟大家聊聊有關(guān)如何理解C++11中Lock,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
詳細介紹一下 C++11 標(biāo)準(zhǔn)的鎖類型。
C++11 標(biāo)準(zhǔn)為我們提供了兩種基本的鎖類型,分別如下:
std::lock_guard,與 Mutex RAII 相關(guān),方便線程對互斥量上鎖。 std::unique_lock,與 Mutex RAII 相關(guān),方便線程對互斥量上鎖,但提供了更好的上鎖和解鎖控制。
另外還提供了幾個與鎖類型相關(guān)的 Tag 類,分別如下:
std::adopt_lock_t,一個空的標(biāo)記類,定義如下:
struct adopt_lock_t {};
該類型的常量對象adopt_lock(adopt_lock 是一個常量對象,定義如下:
constexpr adopt_lock_t adopt_lock {};,// constexpr 是 C++11 中的新關(guān)鍵字)
通常作為參數(shù)傳入給 unique_lock 或 lock_guard 的構(gòu)造函數(shù)。
std::defer_lock_t,一個空的標(biāo)記類,定義如下:
struct defer_lock_t {};
該類型的常量對象 defer_lock(defer_lock 是一個常量對象,定義如下:
constexpr defer_lock_t defer_lock {};,// constexpr 是 C++11 中的新關(guān)鍵字)
通常作為參數(shù)傳入給 unique_lock 或 lock_guard 的構(gòu)造函數(shù)。
std::try_to_lock_t,一個空的標(biāo)記類,定義如下:
struct try_to_lock_t {};
該類型的常量對象 try_to_lock(try_to_lock 是一個常量對象,定義如下:
constexpr try_to_lock_t try_to_lock {};,// constexpr 是 C++11 中的新關(guān)鍵字)
通常作為參數(shù)傳入給 unique_lock 或 lock_guard 的構(gòu)造函數(shù)。后面我們會詳細介紹以上三種 Tag 類型在配合 lock_gurad 與 unique_lock 使用時的區(qū)別。
std::lock_guard 介紹
std::lock_gurad 是 C++11 中定義的模板類。定義如下:
template
lock_guard 對象通常用于管理某個鎖(Lock)對象,因此與 Mutex RAII 相關(guān),方便線程對互斥量上鎖,即在某個 lock_guard 對象的聲明周期內(nèi),它所管理的鎖對象會一直保持上鎖狀態(tài);而 lock_guard 的生命周期結(jié)束之后,它所管理的鎖對象會被解鎖(注:類似 shared_ptr 等智能指針管理動態(tài)分配的內(nèi)存資源 )。
模板參數(shù) Mutex 代表互斥量類型,例如 std::mutex 類型,它應(yīng)該是一個基本的 BasicLockable 類型,標(biāo)準(zhǔn)庫中定義幾種基本的 BasicLockable 類型,分別 std::mutex, std::recursive_mutex, std::timed_mutex,std::recursive_timed_mutex (以上四種類型均已在上一篇博客中介紹)以及 std::unique_lock(本文后續(xù)會介紹 std::unique_lock)。(注:BasicLockable 類型的對象只需滿足兩種操作,lock 和 unlock,另外還有 Lockable 類型,在 BasicLockable 類型的基礎(chǔ)上新增了 try_lock 操作,因此一個滿足 Lockable 的對象應(yīng)支持三種操作:lock,unlock 和 try_lock;最后還有一種 TimedLockable 對象,在 Lockable 類型的基礎(chǔ)上又新增了 try_lock_for 和 try_lock_until 兩種操作,因此一個滿足 TimedLockable 的對象應(yīng)支持五種操作:lock, unlock, try_lock, try_lock_for, try_lock_until)。
在 lock_guard 對象構(gòu)造時,傳入的 Mutex 對象(即它所管理的 Mutex 對象)會被當(dāng)前線程鎖住。在lock_guard 對象被析構(gòu)時,它所管理的 Mutex 對象會自動解鎖,由于不需要程序員手動調(diào)用 lock 和 unlock 對 Mutex 進行上鎖和解鎖操作,因此這也是最簡單安全的上鎖和解鎖方式,尤其是在程序拋出異常后先前已被上鎖的 Mutex 對象可以正確進行解鎖操作,極大地簡化了程序員編寫與 Mutex 相關(guān)的異常處理代碼。
值得注意的是,lock_guard 對象并不負責(zé)管理 Mutex 對象的生命周期,lock_guard 對象只是簡化了 Mutex 對象的上鎖和解鎖操作,方便線程對互斥量上鎖,即在某個 lock_guard 對象的聲明周期內(nèi),它所管理的鎖對象會一直保持上鎖狀態(tài);而 lock_guard 的生命周期結(jié)束之后,它所管理的鎖對象會被解鎖。
std::lock_guard 構(gòu)造函數(shù)
lock_guard 構(gòu)造函數(shù)如下表所示:
explicit lock_guard (mutex_type& m);
lock_guard (mutex_type& m, adopt_lock_t tag);
lock_guard (const lock_guard&) = delete;
locking (1) | adopting (2) | copy [deleted](3) |
---|
locking 初始化lock_guard 對象管理 Mutex 對象 m,并在構(gòu)造時對 m 進行上鎖(調(diào)用 m.lock())。
adopting初始化lock_guard 對象管理 Mutex 對象 m,與 locking 初始化(1) 不同的是, Mutex 對象 m 已被當(dāng)前線程鎖住。
拷貝構(gòu)造lock_guard 對象的拷貝構(gòu)造和移動構(gòu)造(move construction)均被禁用,因此 lock_guard 對象不可被拷貝構(gòu)造或移動構(gòu)造。
我們來看一個簡單的例子(參考):
#include
在 print_thread_id 中,我們首先對 mtx 進行上鎖操作(mtx.lock();),然后用 mtx 對象構(gòu)造一個 lock_guard 對象(std::lock_guard
#include
std::unique_lock 介紹
但是 lock_guard 較大的缺點也是簡單,沒有給程序員提供足夠的靈活度,因此,C++11 標(biāo)準(zhǔn)中定義了另外一個與 Mutex RAII 相關(guān)類 unique_lock,該類與 lock_guard 類相似,也很方便線程對互斥量上鎖,但它提供了更好的上鎖和解鎖控制。
顧名思義,unique_lock 對象以獨占所有權(quán)的方式( unique owership)管理 mutex 對象的上鎖和解鎖操作,所謂獨占所有權(quán),就是沒有其他的 unique_lock 對象同時擁有某個 mutex 對象的所有權(quán)。
在構(gòu)造(或移動(move)賦值)時,unique_lock 對象需要傳遞一個 Mutex 對象作為它的參數(shù),新創(chuàng)建的 unique_lock 對象負責(zé)傳入的 Mutex 對象的上鎖和解鎖操作。
std::unique_lock 對象也能保證在其自身析構(gòu)時它所管理的 Mutex 對象能夠被正確地解鎖(即使沒有顯式地調(diào)用 unlock 函數(shù))。因此,和 lock_guard 一樣,這也是一種簡單而又安全的上鎖和解鎖方式,尤其是在程序拋出異常后先前已被上鎖的 Mutex 對象可以正確進行解鎖操作,極大地簡化了程序員編寫與 Mutex 相關(guān)的異常處理代碼。
值得注意的是,unique_lock 對象同樣也不負責(zé)管理 Mutex 對象的生命周期,unique_lock 對象只是簡化了 Mutex 對象的上鎖和解鎖操作,方便線程對互斥量上鎖,即在某個 unique_lock 對象的聲明周期內(nèi),它所管理的鎖對象會一直保持上鎖狀態(tài);而 unique_lock 的生命周期結(jié)束之后,它所管理的鎖對象會被解鎖,這一點和 lock_guard 類似,但 unique_lock 給程序員提供了更多的自由,我會在下面的內(nèi)容中給大家介紹 unique_lock 的用法。
另外,與 lock_guard 一樣,模板參數(shù) Mutex 代表互斥量類型,例如 std::mutex 類型,它應(yīng)該是一個基本的 BasicLockable 類型,標(biāo)準(zhǔn)庫中定義幾種基本的 BasicLockable 類型,分別 std::mutex, std::recursive_mutex, std::timed_mutex,std::recursive_timed_mutex (以上四種類型均已在上一篇博客中介紹)以及 std::unique_lock(本文后續(xù)會介紹 std::unique_lock)。(注:BasicLockable 類型的對象只需滿足兩種操作,lock 和 unlock,另外還有 Lockable 類型,在 BasicLockable 類型的基礎(chǔ)上新增了 try_lock 操作,因此一個滿足 Lockable 的對象應(yīng)支持三種操作:lock,unlock 和 try_lock;最后還有一種 TimedLockable 對象,在 Lockable 類型的基礎(chǔ)上又新增了 try_lock_for 和 try_lock_until 兩種操作,因此一個滿足 TimedLockable 的對象應(yīng)支持五種操作:lock, unlock, try_lock, try_lock_for, try_lock_until)。
std::unique_lock 構(gòu)造函數(shù)
std::unique_lock 的構(gòu)造函數(shù)的數(shù)目相對來說比 std::lock_guard 多,其中一方面也是因為 std::unique_lock 更加靈活,從而在構(gòu)造 std::unique_lock 對象時可以接受額外的參數(shù)。總地來說,std::unique_lock 構(gòu)造函數(shù)如下:
unique_lock() noexcept;
explicit unique_lock(mutex_type& m);
unique_lock(mutex_type& m, try_to_lock_t tag);
unique_lock(mutex_type& m, defer_lock_t tag) noexcept;
unique_lock(mutex_type& m, adopt_lock_t tag);
template
template
unique_lock(const unique_lock&) = delete;
unique_lock(unique_lock&& x);
default (1) | locking (2) | try-locking (3) | deferred (4) | adopting (5) | locking for (6) | locking until (7) | copy [deleted] (8) | move (9) |
---|
下面我們來分別介紹以上各個構(gòu)造函數(shù):
(1) 默認構(gòu)造函數(shù)新創(chuàng)建的 unique_lock 對象不管理任何 Mutex 對象。(2) locking 初始化新創(chuàng)建的 unique_lock 對象管理 Mutex 對象 m,并嘗試調(diào)用 m.lock() 對 Mutex 對象進行上鎖,如果此時另外某個 unique_lock 對象已經(jīng)管理了該 Mutex 對象 m,則當(dāng)前線程將會被阻塞。(3) try-locking 初始化新創(chuàng)建的 unique_lock 對象管理 Mutex 對象 m,并嘗試調(diào)用 m.try_lock() 對 Mutex 對象進行上鎖,但如果上鎖不成功,并不會阻塞當(dāng)前線程。(4) deferred 初始化新創(chuàng)建的 unique_lock 對象管理 Mutex 對象 m,但是在初始化的時候并不鎖住 Mutex 對象。 m 應(yīng)該是一個沒有當(dāng)前線程鎖住的 Mutex 對象。(5) adopting 初始化新創(chuàng)建的 unique_lock 對象管理 Mutex 對象 m, m 應(yīng)該是一個已經(jīng)被當(dāng)前線程鎖住的 Mutex 對象。(并且當(dāng)前新創(chuàng)建的 unique_lock 對象擁有對鎖(Lock)的所有權(quán))。(6) locking 一段時間(duration)新創(chuàng)建的 unique_lock 對象管理 Mutex 對象 m,并試圖通過調(diào)用 m.try_lock_for(rel_time) 來鎖住 Mutex 對象一段時間(rel_time)。(7) locking 直到某個時間點(time point)新創(chuàng)建的 unique_lock 對象管理 Mutex 對象m,并試圖通過調(diào)用 m.try_lock_until(abs_time) 來在某個時間點(abs_time)之前鎖住 Mutex 對象。(8) 拷貝構(gòu)造 [被禁用]unique_lock 對象不能被拷貝構(gòu)造。(9) 移動(move)構(gòu)造新創(chuàng)建的 unique_lock 對象獲得了由 x 所管理的 Mutex 對象的所有權(quán)(包括當(dāng)前 Mutex 的狀態(tài))。調(diào)用 move 構(gòu)造之后, x 對象如同通過默認構(gòu)造函數(shù)所創(chuàng)建的,就不再管理任何 Mutex 對象了。綜上所述,由 (2) 和 (5) 創(chuàng)建的 unique_lock 對象通常擁有 Mutex 對象的鎖。而通過 (1) 和 (4) 創(chuàng)建的則不會擁有鎖。通過 (3),(6) 和 (7) 創(chuàng)建的 unique_lock 對象,則在 lock 成功時獲得鎖。
關(guān)于unique_lock 的構(gòu)造函數(shù),請看下面例子(參考):
#include
std::unique_lock 移動(move assign)賦值操作
std::unique_lock 支持移動賦值(move assignment),但是普通的賦值被禁用了,
unique_lock& operator= (unique_lock&& x) noexcept;
unique_lock& operator= (const unique_lock&) = delete;
move (1) | copy [deleted] (2) |
---|
移動賦值(move assignment)之后,由 x 所管理的 Mutex 對象及其狀態(tài)將會被新的 std::unique_lock 對象取代。
如果被賦值的對象之前已經(jīng)獲得了它所管理的 Mutex 對象的鎖,則在移動賦值(move assignment)之前會調(diào)用 unlock 函數(shù)釋放它所占有的鎖。
調(diào)用移動賦值(move assignment)之后, x 對象如同通過默認構(gòu)造函數(shù)所創(chuàng)建的,也就不再管理任何 Mutex 對象了。請看下面例子(參考):
#include
std::unique_lock 主要成員函數(shù)
本節(jié)我們來看看 std::unique_lock 的主要成員函數(shù)。由于 std::unique_lock 比 std::lock_guard 操作靈活,因此它提供了更多成員函數(shù)。具體分類如下:
上鎖/解鎖操作:lock,try_lock,try_lock_for,try_lock_until 和 unlock 修改操作:移動賦值(move assignment)(前面已經(jīng)介紹過了),交換(swap)(與另一個 std::unique_lock 對象交換它們所管理的 Mutex 對象的所有權(quán)),釋放(release)(返回指向它所管理的 Mutex 對象的指針,并釋放所有權(quán)) 獲取屬性操作:owns_lock(返回當(dāng)前 std::unique_lock 對象是否獲得了鎖)、operator bool()(與 owns_lock 功能相同,返回當(dāng)前 std::unique_lock 對象是否獲得了鎖)、mutex(返回當(dāng)前 std::unique_lock 對象所管理的 Mutex 對象的指針)。
std::unique_lock::lock請看下面例子(參考):
上鎖操作,調(diào)用它所管理的 Mutex 對象的 lock 函數(shù)。如果在調(diào)用 Mutex 對象的 lock 函數(shù)時該 Mutex 對象已被另一線程鎖住,則當(dāng)前線程會被阻塞,直到它獲得了鎖。
該函數(shù)返回時,當(dāng)前的 unique_lock 對象便擁有了它所管理的 Mutex 對象的鎖。如果上鎖操作失敗,則拋出 system_error 異常。
// unique_lock::lock/unlock#include
std::unique_lock::try_lock
上鎖操作,調(diào)用它所管理的 Mutex 對象的 try_lock 函數(shù),如果上鎖成功,則返回 true,否則返回 false。
請看下面例子(參考):
#include
std::unique_lock::try_lock_for
上鎖操作,調(diào)用它所管理的 Mutex 對象的 try_lock_for 函數(shù),如果上鎖成功,則返回 true,否則返回 false。
請看下面例子(參考):
#include
std::unique_lock::try_lock_until
上鎖操作,調(diào)用它所管理的 Mutex 對象的 try_lock_for 函數(shù),如果上鎖成功,則返回 true,否則返回 false。
請看下面例子(參考):
#include
std::unique_lock::unlock
解鎖操作,調(diào)用它所管理的 Mutex 對象的 unlock 函數(shù)。
請看下面例子(參考):
#include
std::unique_lock::release
返回指向它所管理的 Mutex 對象的指針,并釋放所有權(quán)。
請看下面例子(參考):
#include
std::unique_lock::owns_lock
返回當(dāng)前 std::unique_lock 對象是否獲得了鎖。
請看下面例子(參考):
#include
std::unique_lock::operator bool()
與 owns_lock 功能相同,返回當(dāng)前 std::unique_lock 對象是否獲得了鎖。
請看下面例子(參考):
#include
std::unique_lock::mutex
返回當(dāng)前 std::unique_lock 對象所管理的 Mutex 對象的指針。
請看下面例子(參考):
#include
看完上述內(nèi)容,你們對如何理解C++11中Lock有進一步的了解嗎?如果還想了解更多知識或者相關(guān)內(nèi)容,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。