據我所知,只有condition_variable.wait_for使用謂詞(因為內部仔細檢查)可以避免被虛假喚醒解除阻塞,但沒有謂詞的版本(如果不是while則使用)。
但是,如果我想在僅cv_status::timeout發生的情況下做某事并做其他事情怎么辦notify_XXX?因為condition_variable.wait_for只有謂詞回傳bool,它無法判斷它是否被notify_XXXor解鎖cv_status::timeout;并且雖然condition_variable.wait_for沒有謂詞回傳cv_status::timeout,但它無法判斷它是否被虛假喚醒或notify_XXX.
uj5u.com熱心網友回復:
條件變數最好用作三元組。簡歷、互斥鎖和有效負載。
如果沒有有效負載(隱式或顯式),就無法確定喚醒是否是虛假的。
謂詞版本使檢查有效負載變得容易,但在某些復雜的情況下,如果不使用 lambda 來檢查有效負載可能會更容易。所以提供了其他API。
修改payload后,條件變數操作的互斥體必須處于鎖定狀態,然后才能發送信號。(例如,您可以使用互斥鎖保護有效負載;或者您可以原子地修改有效負載,然后鎖定和解鎖互斥鎖,然后發送信號)。否則,可能會發生與虛假喚醒相反的情況(丟失的信號)。
所有這一切都很難做到正確,而且很容易意外出錯。
如果你想撰寫新的并發代碼(尤其是使用低級原語),你必須學習足夠多的 C 記憶體模型并學習如何證明你的演算法是正確的。因為它是一種很難撰寫代碼并基于“它是否作業”的正確性的方法。
您已經正確地確定,如果沒有額外的資料,您將無法解決這個問題。您需要添加額外的資料,并使用它來確定喚醒是虛假的還是真實的。這是設計使然。
C 可以將附加資料添加到條件變數中,但是即使您不使用它也會讓您為此付費。條件變數是一個低級原語,它可以讓您撰寫盡可能接近最優的代碼,事實上它包裝在一個類中可能會讓某些人感到困惑。
并且有很多有效載荷。如果您有一個計數信號量,其中發送信號的數量與接收信號的數量相匹配,那么您的有效負載將是一個整數。如果你有一個門閂或門,一旦打開,每個人都可以自由通過,你的有效載荷將是一個布林值。
struct gate {
void wait_on_gate() const {
auto l = lock();
cv.wait( l, [&]{ return !closed; } );
}
// false iff it times out
template<class Time>
bool wait_on_gate_until(Time time) const {
auto l = lock();
return cv.wait_until( l, time, [&]{ return !closed; } );
}
// false iff it times out
template<class Duration>
bool wait_on_gate_for(Duration d) const {
auto l = lock();
return cv.wait_for( l, d, [&]{ return !closed; } );
}
// Once you call this, nobody waits
void open_gate() {
auto l = lock();
closed = false;
cv.notify_all();
}
private:
mutable std::mutex m;
std::condition_variable cv;
bool closed = true;
};
現在你會注意到我使用的是 lambda 版本。
我們可以重構為非 lambda 版本:
void wait_on_gate() const {
auto l = lock();
while(closed)
cv.wait( l );
}
template<class Time>
void wait_on_gate_until(Time time) const {
auto l = lock();
while(closed) {
if (cv.wait_until(l, time) == std::cv_status::timeout)
return !closed;
}
return true;
}
它更復雜,并且行為完全相同。(假設我沒有錯別字)。
唯一的區別是你可以做一些可能不適合 lambda 的花哨的事情。例如,您可以選擇說“好吧,這是虛假的,但是當我醒著時,我會去其他地方做一些簿記,稍后再回來”。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/491090.html
