【多线程】1.条件变量--std::condition_variable
條件變量允許我們通過通知進而實現線程同步。
因此,您可以實現發送方/接收方或生產者/消費者之類的工作流。
在這樣的工作流程中,接收者正在等待發送者的通知。如果接收者收到通知,它將繼續工作。
1. std::condition_variable
條件變量可以履行發送者或接收者的角色。
作為發送者,它可以通知一個或多個接收者。
這就是使用條件變量所需要知道的基本所有內容,程序示例:
該程序有兩個子線程: t1和t2。
它們在第33行和第34行中獲得可調用的有效負載(函數或函子) waitingForWork和setDataReady。
函數setDataReady通過使用條件變量condVar調用condVar.notify_one()進行通知。
在持有鎖的同時,線程t1正在等待通知: condVar.wait(lck).
在等待的線程會執行的步驟:
在等待的線程總是會執行相同的步驟:線程醒來 -> 試圖得到鎖 -> 檢查是否持有鎖:
- 如果通知到達,并在獲取鎖失敗的情況下,讓自己回到睡眠狀態;
- 在獲取鎖成功的情況下,線程離開上面的線程醒來 -> 試圖得到鎖 -> 檢查是否持有鎖循環過程并繼續其工作。
該程序的輸出也沒什么意外
但是那是我的第一印象,一個例子有限的測試次數說明不了問題。接下來再看虛假的喚醒…
2. 虛假的喚醒
細節決定成敗。事實上,可能發生的是,接收方在發送方發出通知之前完成了任務。
即接收方被虛假喚醒,然后執行完wait()后的內容,而后發送方才發出通知。虛假喚醒,使得發送方的通知沒了意義,甚至可能出現隱患。
這怎么可能呢?
接收方對虛假的喚醒很敏感。所以即使沒有通知發生,接收方也有可能會醒來。
為了保護它,我不得不向等待方法添加一個判斷。
這就是我在下一個例子中所做的(存在缺陷:可能喚醒不了):
與第一個示例的關鍵區別是在第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的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【C++】31. Boost::circ
- 下一篇: 【数据结构与算法】1.二叉树代码