C++基礎(chǔ)-mutex剖煌,time_mutex, lock_guard, unique_lock

std:mutex

在 C++ 中沥匈,mutex 類能用于保護(hù)共享數(shù)據(jù)從多個(gè)線程同時(shí)訪問的同步原語神帅。

mutex 提供排他性非遞歸所有權(quán)語義:

  • 調(diào)用方線程從它成功調(diào)用 lock 或者 try_lock 開始,到它調(diào)用 unlock 為止胳岂,占用該 mutex
  • 調(diào)用線程占用 mutex编整,所有其它線程試圖要求 mutex 的所有權(quán),如果請求線程調(diào)用 lock(),則將阻塞;如果請求線程調(diào)用 try_lock() 則返回 false乳丰。

如果 mutex 在被線程占用的時(shí)候被銷毀掌测,或者占用 mutex 的時(shí)候線程終止,則行為未定義产园。

mutex 的類信息如下:

(構(gòu)造函數(shù)) 構(gòu)造互斥 (公開成員函數(shù))
(析構(gòu)函數(shù)) 銷毀互斥 (公開成員函數(shù))
operator=[被刪除] 不可復(fù)制賦值 (公開成員函數(shù))
鎖定
lock 鎖定互斥汞斧,若互斥不可用則阻塞 (公開成員函數(shù))
try_lock 嘗試鎖定互斥夜郁,若互斥不可用則返回 (公開成員函數(shù))
unlock 解鎖互斥 (公開成員函數(shù))
原生句柄
native_handle 返回底層實(shí)現(xiàn)定義的原生句柄 (公開成員函數(shù))

在實(shí)際的開發(fā)中,不會直接使用 mutex 粘勒,而是使用 std:unique_lock,std::lock_guard,std::scoped_lock 等方式進(jìn)行加鎖竞端。

std::recursive_mutex

和 std::mutex 一樣,但是 std::recursive_mutex 允許同一個(gè)線程對互斥量多次上鎖庙睡,也就是遞歸上鎖事富,來獲得互斥對象的多層所有權(quán),同時(shí)釋放互斥量也要調(diào)用鎖深度層次相同的 unlock() 方法乘陪。

std::time_mutex

std::time_mutex 比 std::mutex 多了兩個(gè)成員函數(shù)统台,try_lock_for(),try_lock_until()啡邑。

try_lock_for() 表示函數(shù)接受一個(gè)時(shí)間范圍贱勃,也就是在這個(gè)時(shí)間段范圍內(nèi)沒有獲取鎖,則該線程被阻塞谤逼。

try_lock_until 函數(shù)則接受一個(gè)時(shí)間點(diǎn)作為參數(shù)贵扰,在指定時(shí)間點(diǎn)未到來之前線程如果沒有獲得鎖則被阻塞住,如果在此期間其他線程釋放了鎖森缠,則該線程可以獲得對互斥量的鎖拔鹰,如果超時(shí)(即在指定時(shí)間內(nèi)還是沒有獲得鎖)仪缸,則返回 false贵涵。

示例代碼如下:

#include <mutex>
#include <thread>
#include <iostream>

std::timed_mutex t_mutex;
void fire_work() {
    //try_lock_for 會等待一段時(shí)間,
    while (!t_mutex.try_lock_for(chrono::milliseconds(200))) {
        std::cout << "-";
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    cout << "*\n";
    t_mutex.unlock();
}

int main() {
    std::thread threads[10];
    // spawn 10 threads:
    for (int i = 0; i < 10; ++i)
        threads[i] = std::thread(fire_work);

    for (auto &th : threads) th.join();
    return 0;
}

最后控制臺輸入如下:

------------------------------------*
----------------------------------------*
-----------------------------------*
------------------------------*
-------------------------*
--------------------*
---------------*
----------*
-----*
*

示例代碼中恰画,啟動了十個(gè)線程,然后每個(gè)線程都會對互斥信號量 t_mutex宾茂,請求 lock。第一個(gè)線程拴还,拿到了 lock跨晴,然后直接打印了 *,第二個(gè)線程,try_lock 會等待 200 片林。

std::lock_guard

源碼位于頭文件 <mutex> 中端盆,template<class Mutex> class lock_grard

該類是互斥體的包裝器,為在作用域塊期間內(nèi)占用互斥提供便利的 RAII 風(fēng)格機(jī)制费封,也就是資源獲取即初始化機(jī)制焕妙。

從邏輯上來說,創(chuàng)建 lock_guard() 對象時(shí)弓摘,它試圖接受給定互斥的所有權(quán)焚鹊。控制離開創(chuàng)建 lock_guard 對象的作用域時(shí)韧献,銷毀 lock_guard 并且是否互斥末患。

簡單例子如下:

#include <mutex>
#include <thread>
//聲明空間
using namespace std;

int g_i = 0;
mutex g_i_mutex;//定義一個(gè)互斥信息

void safe_increment() {
    //創(chuàng)建 lock_guard 對象研叫,并且使用g_i_mutex 作為構(gòu)造函數(shù)參數(shù)
    //在整個(gè)方法作用域里面,這鎖都是生效的
    lock_guard<std::mutex> lock(g_i_mutex);
    //操作需要互斥的變量之前璧针,先創(chuàng)建 lock_guard<std::mutex>  對象
    g_i++;
}


int main() {
    thread t1(safe_increment);
    thread t2(safe_increment);
    t1.join();
    t2.join();
}

lock_grard 的原理很簡單嚷炉,在對象的構(gòu)造函數(shù)中,調(diào)用 mutex.lock()探橱,然后在析構(gòu)函數(shù)中渤昌,調(diào)用 unlock(),源碼如下:

explicit lock_guard(mutex_type& __m) _LIBCPP_THREAD_SAFETY_ANNOTATION(acquire_capability(__m))
    : __m_(__m) {__m_.lock();}

lock_guard(mutex_type& __m, adopt_lock_t) _LIBCPP_THREAD_SAFETY_ANNOTATION(requires_capability(__m))
    : __m_(__m) {}
~lock_guard() _LIBCPP_THREAD_SAFETY_ANNOTATION(release_capability()) {__m_.unlock();}

std::unique_lock

源碼位于頭文件 <mutex> 中,template<class Mutex> class unique_lock

和 std::lock_guard() 類似走搁,但是提供了更好的上鎖機(jī)制和解鎖控制独柑。包含了 try_lock_for() 和 try_lock_unitl() 兩個(gè)方法,可以指定等待鎖的時(shí)間私植。

類的相關(guān)信息如下:

(構(gòu)造函數(shù)) 構(gòu)造 unique_lock 忌栅,可選地鎖定提供的互斥 (公開成員函數(shù))
(析構(gòu)函數(shù)) 若占有關(guān)聯(lián)互斥,則解鎖之 (公開成員函數(shù))
operator= 若占有則解鎖互斥曲稼,并取得另一者的所有權(quán) (公開成員函數(shù))
鎖定
lock 鎖定關(guān)聯(lián)互斥 (公開成員函數(shù))
try_lock 嘗試鎖定關(guān)聯(lián)互斥索绪,若互斥不可用則返回 (公開成員函數(shù))
try_lock_for 試圖鎖定關(guān)聯(lián)的可定時(shí)鎖定 (TimedLockable) 互斥,若互斥在給定時(shí)長中不可用則返回 (公開成員函數(shù))
try_lock_until 嘗試鎖定關(guān)聯(lián)可定時(shí)鎖定 (TimedLockable) 互斥贫悄,若抵達(dá)指定時(shí)間點(diǎn)互斥仍不可用則返回 (公開成員函數(shù))
unlock 解鎖關(guān)聯(lián)互斥 (公開成員函數(shù))
修改器
swap 與另一 std::unique_lock 交換狀態(tài) (公開成員函數(shù))
release 將關(guān)聯(lián)互斥解關(guān)聯(lián)而不解鎖它 (公開成員函數(shù))
觀察器
mutex 返回指向關(guān)聯(lián)互斥的指針 (公開成員函數(shù))
owns_lock 測試鎖是否占有其關(guān)聯(lián)互斥 (公開成員函數(shù))
operator bool 測試鎖是否占有其關(guān)聯(lián)互斥 (公開成員函數(shù))

示例代碼如下瑞驱,以一個(gè)銀行轉(zhuǎn)賬的業(yè)務(wù)為例子:

void transfer(bank_account &from, bank_account &to, int amount) {
    //defer_lock 表示延遲加鎖,實(shí)際上未獲取鎖
    unique_lock<mutex> lock1(from.mMutex, defer_lock);
    unique_lock<mutex> lock2(to.mMutex, defer_lock);
    //獲取鎖
    lock(lock1, lock2);
    from.iMoney -= amount;
    to.iMoney += amount;
}

void test_transfer() {
    bank_account Account1("User1", 100);
    bank_account Account2("User2", 50);
    thread t1([&]() { transfer(Account1, Account2, 10); });//lambda表達(dá)式
    thread t2([&]() { transfer(Account2, Account1, 5); });
    t1.join();
    t2.join();
}

參考

RAII

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市窄坦,隨后出現(xiàn)的幾起案子唤反,更是在濱河造成了極大的恐慌,老刑警劉巖鸭津,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件彤侍,死亡現(xiàn)場離奇詭異,居然都是意外死亡逆趋,警方通過查閱死者的電腦和手機(jī)盏阶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闻书,“玉大人名斟,你說我怎么就攤上這事∑敲迹” “怎么了砰盐?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長杆融。 經(jīng)常有香客問我楞卡,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任蒋腮,我火速辦了婚禮淘捡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘池摧。我一直安慰自己焦除,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布作彤。 她就那樣靜靜地躺著膘魄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪竭讳。 梳的紋絲不亂的頭發(fā)上创葡,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機(jī)與錄音绢慢,去河邊找鬼灿渴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛胰舆,可吹牛的內(nèi)容都是我干的骚露。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼缚窿,長吁一口氣:“原來是場噩夢啊……” “哼棘幸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起倦零,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤误续,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后光绕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體女嘲,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡畜份,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年诞帐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片爆雹。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡停蕉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出钙态,到底是詐尸還是另有隱情慧起,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布册倒,位于F島的核電站蚓挤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜灿意,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一估灿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧缤剧,春花似錦馅袁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至抵窒,卻和暖如春弛针,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背李皇。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工钦奋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人疙赠。 一個(gè)月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓付材,卻偏偏與公主長得像,于是被迫代替她去往敵國和親圃阳。 傳聞我的和親對象是個(gè)殘疾皇子厌衔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容