日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【多线程】1.条件变量--std::condition_variable

發布時間:2025/3/21 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【多线程】1.条件变量--std::condition_variable 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

條件變量允許我們通過通知進而實現線程同步。
因此,您可以實現發送方/接收方或生產者/消費者之類的工作流。
在這樣的工作流程中,接收者正在等待發送者的通知。如果接收者收到通知,它將繼續工作。


1. std::condition_variable

條件變量可以履行發送者或接收者的角色。
作為發送者,它可以通知一個或多個接收者。
這就是使用條件變量所需要知道的基本所有內容,程序示例:

// conditionVariable.cpp#include <iostream> #include <condition_variable> #include <mutex> #include <thread>std::mutex mutex_; std::condition_variable condVar;void doTheWork(){std::cout << "Processing shared data." << std::endl; }void waitingForWork(){std::cout << "Worker: Waiting for work." << std::endl;std::unique_lock<std::mutex> lck(mutex_);condVar.wait(lck);doTheWork();std::cout << "Work done." << std::endl; }void setDataReady(){std::cout << "Sender: Data is ready." << std::endl;condVar.notify_one(); }int main(){std::cout << std::endl;std::thread t1(waitingForWork);std::thread t2(setDataReady);t1.join();t2.join();std::cout << std::endl;}

該程序有兩個子線程: t1和t2。
它們在第33行和第34行中獲得可調用的有效負載(函數或函子) waitingForWork和setDataReady。
函數setDataReady通過使用條件變量condVar調用condVar.notify_one()進行通知。
在持有鎖的同時,線程t1正在等待通知: condVar.wait(lck).

在等待的線程會執行的步驟:

在等待的線程總是會執行相同的步驟:線程醒來 -> 試圖得到鎖 -> 檢查是否持有鎖:

  • 如果通知到達,并在獲取鎖失敗的情況下,讓自己回到睡眠狀態;
  • 在獲取鎖成功的情況下,線程離開上面的線程醒來 -> 試圖得到鎖 -> 檢查是否持有鎖循環過程并繼續其工作。


該程序的輸出也沒什么意外


但是那是我的第一印象,一個例子有限的測試次數說明不了問題。接下來再看虛假的喚醒…

2. 虛假的喚醒

細節決定成敗。事實上,可能發生的是,接收方在發送方發出通知之前完成了任務。
即接收方被虛假喚醒,然后執行完wait()后的內容,而后發送方才發出通知。虛假喚醒,使得發送方的通知沒了意義,甚至可能出現隱患。


這怎么可能呢?

接收方對虛假的喚醒很敏感。所以即使沒有通知發生,接收方也有可能會醒來。
為了保護它,我不得不向等待方法添加一個判斷。


這就是我在下一個例子中所做的(存在缺陷:可能喚醒不了):

// conditionVariableFixed.cpp#include <iostream> #include <condition_variable> #include <mutex> #include <thread>std::mutex mutex_; std::condition_variable condVar;bool dataReady;void doTheWork(){std::cout << "Processing shared data." << std::endl; }void waitingForWork(){std::cout << "Worker: Waiting for work." << std::endl;std::unique_lock<std::mutex> lck(mutex_);condVar.wait(lck,[]{return dataReady;});doTheWork();std::cout << "Work done." << std::endl; }void setDataReady(){std::lock_guard<std::mutex> lck(mutex_);dataReady=true;std::cout << "Sender: Data is ready." << std::endl;condVar.notify_one(); }int main(){std::cout << std::endl;std::thread t1(waitingForWork);std::thread t2(setDataReady);t1.join();t2.join();std::cout << std::endl;}

與第一個示例的關鍵區別是在第11行中使用了一個布爾變量dataReady 作為附加條件。
dataReady在第28行中被設置為true。

它在函數waitingForWork中被檢查:

condVar.wait(lck,[]{return dataReady;})

這就是為什么wait方法有一個額外的重載,它接受一個判定。判定是個callable,它返回true或false。
在此示例中,callable是lambda函數。因此,條件變量檢查兩個條件:判定是否為真,通知是否發生。

關于dataReady:

dataReady是個共享變量,將會被改變。所以我不得不用鎖來保護它。
因為線程t2只設置和釋放鎖一次,所以std::lock_guard已經夠用了。但是線程t1就不行了,wait方法將持續鎖定和解鎖互斥體(原因點擊此處,參考“在等待的線程會執行的步驟”)。
所以我需要更強大的鎖:std::unique_lock。
但這還不是全部,條件變量有很多挑戰,它們必須用鎖來保護,并且易受虛假喚醒的影響。
大多數用例都很容易用tasks來解決,后續再說task問題。


  • 虛假喚醒: 接收方在發送方發出通知之前完成了任務, 即,線程t1先結束wait,喚醒線程t1完成任務,然后線程t2才發送通知。
  • 喚醒不了: 發送方在接收方進入等待wait狀態之前發送通知,則通知會丟失。即,先通知,后等待。

3. 喚醒不了

條件變量的異常行為還是有的。大約每10次執行一次conditionVariable.cpp就會發生一些奇怪的現象:

為什么線程t2明明notify_one通知了,線程t1卻還一直在wait阻塞等待?

我開始不知道怎么回事,這種現象完全違背了我對條件變量的直覺。
在安東尼·威廉姆斯的支持下,我解開了謎團。


問題在于:
如果發送方在接收方進入等待狀態之前發送通知,則通知會丟失。所以需要線程t1的 wait等待的語句先執行到wait位置,線程t2的notify_one語句后執行,才能喚醒t1的wait。C ++標準同時也將條件變量描述為同步機制,condition_variable類是一個同步原語,可以用來同時阻塞一個線程或多個線程…”

因此,通知消息已經丟失了,但是接收方還在等啊和等啊等啊等啊…

怎么解決這個問題呢?
答:去除掉wait第二個參數的判定可以有效幫助喚醒。實際上,在判定設置為真的情況下,接收器也能夠獨立于發送者的通知進而繼續其工作。


譯者水平有限,大多谷歌翻譯,看不懂的請看原文地址。對于部分人,我又不賺你什么錢,原文也已經附上了,你在噴什么shit?你自己貢獻了什么?給對你有用的博文點過贊?

原文地址:http://www.modernescpp.com/index.php/condition-variables

總結

以上是生活随笔為你收集整理的【多线程】1.条件变量--std::condition_variable的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。