C++多線程中的鎖和條件變量使用

在做多線程編程時(shí),有兩個(gè)場(chǎng)景我們都會(huì)遇到:

  1. 多線程訪問(wèn)共享資源,需要用到鎖茫因;
  2. 多線程間的狀態(tài)同步,這個(gè)可用的機(jī)制很多杖剪,條件變量是廣泛使用的一種。

今天我用一個(gè)簡(jiǎn)單的例子來(lái)給大家介紹下鎖和條件變量的使用驰贷。

代碼使用C++11

示例代碼

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

std::mutex              g_mutex;     // 用到的全局鎖
std::condition_variable g_cond;      // 用到的條件變量

int  g_i       = 0;
bool g_running = true;

void ThreadFunc(int n) {             // 線程執(zhí)行函數(shù)
  for (int i = 0; i < n; ++i) {
    {
      std::lock_guard<std::mutex> lock(g_mutex);      // 加鎖盛嘿,離開(kāi){}作用域后鎖釋放
      ++g_i;
      std::cout << "plus g_i by func thread " << std::this_thread::get_id() << std::endl;
    }
  }

  std::unique_lock<std::mutex> lock(g_mutex);        // 加鎖
  while (g_running) {
    std::cout << "wait for exit" << std::endl;
    g_cond.wait(lock);                               // wait調(diào)用后,會(huì)先釋放鎖括袒,之后進(jìn)入等待狀態(tài)次兆;當(dāng)其它進(jìn)程調(diào)用通知激活后,會(huì)再次加鎖
  }

  std::cout << "func thread exit" << std::endl;
}

int main() {
  int         n = 100;
  std::thread t1(ThreadFunc, n);       // 創(chuàng)建t1線程(func thread)锹锰,t1會(huì)執(zhí)行`ThreadFunc`中的指令

  for (int i = 0; i < n; ++i) {
    {
      std::lock_guard<std::mutex> lock(g_mutex);
      ++g_i;
      std::cout << "plus g_i by main thread " << std::this_thread::get_id() << std::endl;
    }
  }

  {
    std::lock_guard<std::mutex> lock(g_mutex);
    g_running = false;
    g_cond.notify_one();      // 通知其它線程
  }

  t1.join();         // 等待線程t1結(jié)束

  std::cout << "g_i = " << g_i << std::endl;
}

程序運(yùn)行后芥炭,關(guān)鍵輸出如下:

plus g_i by main thread 139921025066816
plus g_i by main thread 139921025066816
plus g_i by func thread 139921006847744
plus g_i by func thread 139921006847744
plus g_i by func thread 139921006847744
plus g_i by func thread 139921006847744
plus g_i by func thread 139921006847744
wait for exit                             // func thread等待main thread發(fā)來(lái)的退出信號(hào)
plus g_i by main thread 139921025066816
plus g_i by main thread 139921025066816
plus g_i by main thread 139921025066816
plus g_i by main thread 139921025066816
plus g_i by main thread 139921025066816
plus g_i by main thread 139921025066816
plus g_i by main thread 139921025066816
plus g_i by main thread 139921025066816
plus g_i by main thread 139921025066816
plus g_i by main thread 139921025066816
func thread exit
g_i = 200          // 鎖機(jī)制保證了g_i的正確

可以看到:

  1. 兩個(gè)線程有各自的線程id(thread id),通過(guò)std::this_thread::get_id()獲得恃慧;
  2. 兩個(gè)線程交替執(zhí)行园蝠,需要有先后順序時(shí),就可以通過(guò)條件變量這種機(jī)制來(lái)做到痢士;
  3. 通過(guò)鎖機(jī)制(mutex)保證了g_i計(jì)算結(jié)果的正確

加鎖方法介紹

加鎖相關(guān)的代碼為:

{
  std::lock_guard<std::mutex> lock(g_mutex);
  ......
}

要點(diǎn)為:

  1. 首先,這在一個(gè)局部作用域內(nèi),std::lock_guard在構(gòu)造時(shí)伦籍,會(huì)調(diào)用g_mutex->lock()方法搀愧;
  2. 局部作用域代碼結(jié)束后,std:;lock_guard的析構(gòu)函數(shù)會(huì)被調(diào)用城侧,函數(shù)中會(huì)調(diào)用g_mutex->unlock()方法易遣。

這樣就實(shí)現(xiàn)了加鎖和解鎖的過(guò)程,為什么不直接調(diào)用加鎖解鎖方法呢嫌佑?

我想豆茫,這是因?yàn)槿绻渔i和解鎖中間的代碼出現(xiàn)了問(wèn)題,導(dǎo)致線程函數(shù)異常退出屋摇,那么這個(gè)鎖就一直無(wú)法得到釋放澜薄,其它線程處理的不好的話,就會(huì)造成死鎖了摊册。

條件變量使用介紹

  1. 當(dāng)線程調(diào)用g_cond.wait(lock)前要先手動(dòng)調(diào)用lock->lock()肤京,這里是通過(guò)std::unique_lock的構(gòu)造方法實(shí)現(xiàn)的;
  2. 當(dāng)線程調(diào)用g_cond.wait(lock)進(jìn)入等待后,會(huì)調(diào)用lock->unlock()方法忘分,所以這也是前面構(gòu)造lock時(shí)使用了std::unique_lock;
  3. 通知使用的g_cond.notify_one()棋枕,這個(gè)可以通知一個(gè)線程,另外還有g_cond.notify_all()用于通知所有線程妒峦;
  4. 線程收到通知的代碼放在一個(gè)while循環(huán)中重斑,這是為了防止APUE中提到的虛假通知。

結(jié)束語(yǔ)

上面是我對(duì)C++11中多線程加鎖和條件變量使用的基本認(rèn)識(shí)肯骇,有不當(dāng)?shù)牡胤娇耍€望指正。

參考

cppreference:https://en.cppreference.com/w/cpp/thread

APUE:https://book.douban.com/subject/1439495/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末笛丙,一起剝皮案震驚了整個(gè)濱河市漾脂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌胚鸯,老刑警劉巖骨稿,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異姜钳,居然都是意外死亡坦冠,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門哥桥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)辙浑,“玉大人,你說(shuō)我怎么就攤上這事拟糕±埽” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵已卸,是天一觀的道長(zhǎng)佛玄。 經(jīng)常有香客問(wèn)我,道長(zhǎng)累澡,這世上最難降的妖魔是什么梦抢? 我笑而不...
    開(kāi)封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮愧哟,結(jié)果婚禮上奥吩,老公的妹妹穿的比我還像新娘。我一直安慰自己蕊梧,他們只是感情好霞赫,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著肥矢,像睡著了一般端衰。 火紅的嫁衣襯著肌膚如雪叠洗。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天旅东,我揣著相機(jī)與錄音灭抑,去河邊找鬼。 笑死抵代,一個(gè)胖子當(dāng)著我的面吹牛腾节,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播荤牍,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼案腺,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了康吵?” 一聲冷哼從身側(cè)響起劈榨,我...
    開(kāi)封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎涎才,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體力九,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡耍铜,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了跌前。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棕兼。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖抵乓,靈堂內(nèi)的尸體忽然破棺而出伴挚,到底是詐尸還是另有隱情,我是刑警寧澤灾炭,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布茎芋,位于F島的核電站,受9級(jí)特大地震影響蜈出,放射性物質(zhì)發(fā)生泄漏田弥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一铡原、第九天 我趴在偏房一處隱蔽的房頂上張望偷厦。 院中可真熱鬧,春花似錦燕刻、人聲如沸只泼。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)请唱。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間籍滴,已是汗流浹背酪夷。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留孽惰,地道東北人晚岭。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像勋功,于是被迫代替她去往敵國(guó)和親坦报。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

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