成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

使用條件變量的坑你知道嗎

開發(fā) 前端
想必大家開發(fā)過程中都會用到多線程,用到多線程基本上都會用到條件變量,你理解的條件變量只是簡單的wait和notify嗎,其實這里面還是有一些坑的,程序喵這里總結(jié)給大家。

想必大家開發(fā)過程中都會用到多線程,用到多線程基本上都會用到條件變量,你理解的條件變量只是簡單的wait和notify嗎,最近工作中看同事也都只是簡單的使用wait和notify,導(dǎo)致項目出現(xiàn)bug卻不知如何fix bug,其實這里面還是有一些坑的,程序喵這里總結(jié)給大家。

[[345006]]

1. 什么是條件變量?

條件變量是多線程程序中用來實現(xiàn)等待和喚醒邏輯常用的方法。通常有wait和notify兩個動作,wait用于阻塞掛起線程A,直到另一個線程B通過通過notify喚醒線程A,喚醒后線程A會繼續(xù)運行。

條件變量在多線程中很常用,在有名的生產(chǎn)者和消費者問題中,消費者如何知道生成者是否生產(chǎn)出了可以消費的產(chǎn)品,通過while循環(huán)不停的去判斷是否有可消費的產(chǎn)品?眾所周知,死循環(huán)極其消耗CPU性能,所以需要使用條件變量來阻塞線程,降低CPU占用率。

2. 條件變量的使用

拿生產(chǎn)者和消費者問題舉例,看下面這段代碼:

  1. std::mutex mutex; 
  2. std::condition_variable cv; 
  3. std::vector<int> vec; 
  4.  
  5. void Consume() { 
  6.   std::unique_lock<std::mutex> lock(mutex); 
  7.   cv.wait(lock); 
  8.   std::cout << "consume " << vec.size() << "\n"; 
  9.  
  10. void Produce() { 
  11.   std::unique_lock<std::mutex> lock(mutex); 
  12.   vec.push_back(1); 
  13.   cv.notify_all(); 
  14.   std::cout << "produce \n"; 
  15.  
  16. int main() { 
  17.   std::thread t(Consume); 
  18.   t.detach(); 
  19.   Produce(); 
  20.   return 0; 

本意是消費者線程阻塞,等待生產(chǎn)者生產(chǎn)數(shù)據(jù)后去通知消費者線程,這樣消費者線程就可以拿到數(shù)據(jù)去消費。

但這里有個問題:

如果先執(zhí)行的Produce(),后執(zhí)行的Consume(),生產(chǎn)者提前生產(chǎn)出了數(shù)據(jù),去通知消費者,但是此時消費者線程如果還沒有執(zhí)行到wait語句,即線程還沒有處于掛起等待狀態(tài),線程沒有等待此條件變量上,那通知的信號就丟失了,后面Consume()中才執(zhí)行wait處于等待狀態(tài),但此時生產(chǎn)者已經(jīng)不會再觸發(fā)notify,那消費者線程就會始終阻塞下去,出現(xiàn)bug。

如何解決這個問題呢?可以附加一個判斷條件,就可以解決這種信號丟失問題,見代碼:

  1. std::mutex mutex; 
  2. std::condition_variable cv; 
  3. std::vector<int> vec; 
  4.  
  5. void Consumer() { 
  6.   std::unique_lock<std::mutex> lock(mutex); 
  7.   if (vec.empty()) { // 加入此判斷條件 
  8.       cv.wait(lock); 
  9.   } 
  10.   std::cout << "consumer " << vec.size() << "\n"; 
  11.  
  12. void Produce() { 
  13.   std::unique_lock<std::mutex> lock(mutex); 
  14.   vec.push_back(1); 
  15.   cv.notify_all(); 
  16.   std::cout << "produce \n"; 
  17.  
  18. int main() { 
  19.   std::thread t(Consumer); 
  20.   t.detach(); 
  21.   Produce(); 
  22.   return 0; 

通過增加附加條件可以解決信號丟失的問題,但這里還有個地方需要注意,消費者線程處于wait阻塞狀態(tài)時,即使沒有調(diào)用notify,操作系統(tǒng)也會有一些概率會喚醒處于阻塞的線程,使其繼續(xù)執(zhí)行下去,這就是虛假喚醒問題,當(dāng)出現(xiàn)了虛假喚醒后,消費者線程繼續(xù)執(zhí)行,還是沒有可以消費的數(shù)據(jù),出現(xiàn)了bug。

那怎么解決虛假喚醒的問題呢,可以在線程由阻塞狀態(tài)被喚醒后繼續(xù)判斷附加條件,看是否滿足喚醒的條件,如果滿足則繼續(xù)執(zhí)行,如果不滿足,則繼續(xù)去等待,體現(xiàn)在代碼中,即將if判斷改為while循環(huán)判斷,見代碼:

  1. std::mutex mutex; 
  2. std::condition_variable cv; 
  3. std::vector<int> vec; 
  4.  
  5. void Consumer() { 
  6.   std::unique_lock<std::mutex> lock(mutex); 
  7.   while (vec.empty()) { // 將if改為while 
  8.       cv.wait(lock); 
  9.   } 
  10.   std::cout << "consumer " << vec.size() << "\n"; 
  11.  
  12. void Produce() { 
  13.   std::unique_lock<std::mutex> lock(mutex); 
  14.   vec.push_back(1); 
  15.   cv.notify_all(); 
  16.   std::cout << "produce \n"; 
  17.  
  18. int main() { 
  19.   std::thread t(Consumer); 
  20.   t.detach(); 
  21.   Produce(); 
  22.   return 0; 

看到這里相信你已經(jīng)明白條件變量的使用啦,需要使用while循環(huán)附加判斷條件來解決條件變量的信號丟失和虛假喚醒問題。

3. 有沒有更簡單的“避坑”方式

難道我們每次都必須要使用while循環(huán)和附加條件來操作條件變量嗎?這豈不是很麻煩?

NO!

在C++中其實有更好的封裝,只需要調(diào)用wait函數(shù)時,在參數(shù)中直接添加附加條件就好了,內(nèi)部已經(jīng)做好了while循環(huán)判斷,直接使用即可,見代碼:

  1. std::mutex mutex; 
  2. std::condition_variable cv; 
  3. std::vector<int> vec; 
  4.  
  5. void Consumer() { 
  6.   std::unique_lock<std::mutex> lock(mutex); 
  7.   cv.wait(lock, [&](){ return !vec.empty(); }); // 這里可以直接使用C++的封裝 
  8.   std::cout << "consumer " << vec.size() << "\n"; 
  9.  
  10. void Produce() { 
  11.   std::unique_lock<std::mutex> lock(mutex); 
  12.   vec.push_back(1); 
  13.   cv.notify_all(); 
  14.   std::cout << "produce \n"; 
  15.  
  16. int main() { 
  17.   std::thread t(Consumer); 
  18.   t.detach(); 
  19.   Produce(); 
  20.   return 0; 

但在C語言中就沒辦法啦,大家只能自己做一層封裝啦。

4. 為什么條件變量需要和鎖配合使用?

為什么叫條件變量呢?

因為內(nèi)部是通過判斷及修改某個全局變量來決定線程的阻塞與喚醒,多線程操作同一個變量肯定需要加鎖來使得線程安全。同時,一個簡單的wait函數(shù)調(diào)用內(nèi)部會很復(fù)雜的,有可能線程A調(diào)用了wait函數(shù)但是還沒有進(jìn)入到wait阻塞等待前,另一個線程B在此時卻調(diào)用了notify函數(shù),此時nofity的信號就丟失啦,如果加了鎖,線程B必須等待線程A釋放了鎖并進(jìn)入了等待狀態(tài)后才可以調(diào)用notify,繼而防止信號丟失。

關(guān)于條件變量就介紹到這里,希望大家能有所收獲,平時使用過程中可以避掉條件變量的坑。

 

責(zé)任編輯:趙寧寧 來源: 程序喵大人
相關(guān)推薦

2022-03-10 08:25:27

JavaScrip變量作用域

2020-12-24 15:26:07

Redis數(shù)據(jù)庫

2022-09-14 08:11:06

分頁模糊查詢

2023-08-29 09:31:01

Scrapy網(wǎng)頁爬蟲

2023-12-12 08:41:01

2020-11-26 07:48:24

Shell 腳本內(nèi)置

2019-12-12 09:23:29

Hello World操作系統(tǒng)函數(shù)庫

2023-04-26 10:21:04

2023-12-20 08:23:53

NIO組件非阻塞

2024-04-30 09:02:48

2022-11-22 08:01:34

dotNET 7API

2023-11-06 18:33:19

分庫分表數(shù)據(jù)庫

2021-10-14 06:52:47

算法校驗碼結(jié)構(gòu)

2022-09-29 15:32:58

云計算計算模式

2024-09-18 07:00:00

消息隊列中間件消息隊列

2019-01-07 13:01:08

Linux驚嘆用法命令

2023-02-28 07:39:18

2016-11-22 15:43:13

機房制冷發(fā)展史

2025-01-16 16:41:00

ObjectConditionJDK

2024-10-05 00:00:00

HTTPS性能HTTP/2
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 国产高潮好爽受不了了夜夜做 | 亚洲精品68久久久一区 | 一级片av | 在线观看亚洲精品 | 麻豆91精品91久久久 | 欧美一级三级在线观看 | 特级毛片| 毛片在线看片 | 国产午夜精品一区二区三区四区 | 91日韩| 日韩中文字幕免费在线 | 99久久国产综合精品麻豆 | 国产伦精品 | 91久久综合亚洲鲁鲁五月天 | 久久99久久99 | 久久福利电影 | 欧美性猛交一区二区三区精品 | 国产成人精品999在线观看 | 日韩av手机在线观看 | 亚洲一区二区三区乱码aⅴ 四虎在线视频 | 国产一区二区在线播放 | 精品久久久久久久久久久下田 | 日韩在线第一 | 在线啊v | 亚洲欧美在线观看 | 久久久夜色精品亚洲 | 午夜精品久久久久久久久久久久久 | 国产一区二区影院 | 国产精品久久久久久久久图文区 | 一本在线 | 午夜精品视频 | 一区二区国产在线 | 日韩三级免费网站 | 一区二区三区四区在线视频 | 国产欧美日韩一区 | 日日精品| 成人在线精品 | 成人国产精品免费观看 | 国产精品久久久av | 在线亚洲免费视频 | 一区二区在线观看免费视频 |