php 语法 条件变量,C ++核心准则:注意条件变量的陷阱
今天,我寫了一篇關于條件變量的恐怖文章。您應該意識到條件變量的這一問題。C ++核心準則CP 42僅聲明:“不要無條件等待”。
等待!條件變量支持一個非常簡單的概念。一個線程準備一些東西并發送通知,另一個線程正在等待。為什么不能這么危險?好吧,讓我們從今天的唯一規則開始。
這是該規則的基本原理:“沒有條件的等待可能會錯過喚醒或僅醒來就發現沒有工作要做。”?這意味著什么?條件變量可能是兩個非常嚴重的問題的受害者:喚醒丟失和偽喚醒。關于條件變量的關鍵問題是它們沒有記憶(memory)。
在我向您介紹此問題之前,請先讓我正確進行操作。這是模式,如何使用條件變量。
// conditionVariables.cpp
#include
#include
#include
std::mutex mutex_;
std::condition_variable condVar;
bool dataReady{false};
void waitingForWork(){
std::cout << "Waiting " << std::endl;
std::unique_lock<:mutex> lck(mutex_);
condVar.wait(lck, []{ return dataReady; });? // (4)
std::cout << "Running " << std::endl;
}
void setDataReady(){
{
std::lock_guard<:mutex> lck(mutex_);
dataReady = true;
}
std::cout << "Data prepared" << std::endl;
condVar.notify_one();? ? ? ? ? ? ? ? ? ? ? ? // (3)
}
int main(){
std::cout << std::endl;
std::thread t1(waitingForWork);? ? ? ? ? ? ? // (1)
std::thread t2(setDataReady);? ? ? ? ? ? ? ? // (2)
t1.join();
t2.join();
std::cout << std::endl;
}
同步如何工作?該程序有兩個子線程:t1和t2。他們?在第(1和2)行中獲得了工作包?waitingForWork?和setDataRead。setDataReady?通知-使用條件變量condVar?-即它與工作的準備完成:condVar.notify_one()?(第3行)。在持有鎖的同時,線程t1?等待其通知:condVar.wait(lck,[] {return dataReady;})(第4行)。發送者和接收者需要鎖。對于發送者,則為std :: lock_guard?足夠了,因為它只調用一次鎖定和解鎖。對于接收器,std :: unique_lock?是必需的,因為它通常會頻繁地鎖定和解鎖其互斥鎖。
這是程序的輸出。
也許您在想:為什么您需要一個謂詞才能進行wait調用,因為您可以在?沒有謂詞的情況下調用wait?對于如此簡單的線程同步,此工作流程似乎過于復雜。
現在我們回到memory的丟失中,這兩種現象稱為丟失喚醒和偽喚醒。
丟失的喚醒和虛假的喚醒
喚醒丟失:喚醒丟失的現象是發送方在接收方進入其等待狀態之前發送其通知。結果是通知丟失。C ++標準描述條件變量作為同時同步機制:"The condition_variable class is a synchronisation primitive that can be used to block a thread, or multiple threads?at the same time, ..."。因此通知丟失了,接收方正在等待,并且等待...。
虛假喚醒:盡管沒有發送通知,但接收器可能會喚醒。至少,POSIX Threads?和Windows API可能成為這些現象的受害者。
為了不成為這兩個問題的受害者,您必須使用其他謂詞作為記憶(memory)。或按規則規定是附加條件。如果您不相信,這里就是等待工作流程。
等待工作流程
在等待的初始處理中,線程將鎖定互斥鎖,然后檢查謂詞[] {return dataReady;。}。
如果謂詞的調用評估為
true:線程繼續其工作。
false:condVar.wait()?解鎖互斥鎖并將線程置于等待(阻塞)狀態
如果condition_variable?condVar處于等待狀態并收到通知或虛假喚醒,則會發生以下步驟。
線程被解除阻止,并將重新獲取互斥鎖。
線程檢查謂詞。
如果謂詞的調用評估為
true:線程繼續其工作。
false:condVar.wait()解鎖互斥鎖,并將線程置于等待(阻塞)狀態。
復雜!對?你不相信我嗎
沒有謂詞
如果我從上一個示例中刪除謂詞,將會發生什么?
// conditionVariableWithoutPredicate.cpp
#include
#include
#include
std::mutex mutex_;
std::condition_variable condVar;
void waitingForWork(){
std::cout << "Waiting " << std::endl;
std::unique_lock<:mutex> lck(mutex_);
condVar.wait(lck);? ? ? ? ? ? ? ? ? ? ? // (1)
std::cout << "Running " << std::endl;
}
void setDataReady(){
std::cout << "Data prepared" << std::endl;
condVar.notify_one();? ? ? ? ? ? ? ? ? // (2)
}
int main(){
std::cout << std::endl;
std::thread t1(waitingForWork);
std::thread t2(setDataReady);
t1.join();
t2.join();
std::cout << std::endl;
}
現在,第(1)行中的wait調用不使用謂詞,并且同步看起來非常容易。遺憾地說,但現在的程序有一個競爭條件,你可以在第一個執行看到。屏幕截圖顯示了死鎖。
發送方在接收方能夠接收之前,在第(1)行(condVar.notify_one())中發送其通知;因此,接收器將永遠休眠。
好的,教訓是艱難的。謂詞是必要的,但必須有一種方法可以簡化程序的條件。
原子謂詞
也許您已經看到了。變量dataReady只是一個布爾值。我們應該將其設為原子布爾值,并因此擺脫發送方上的互斥量。
我們來了:
// conditionVariableAtomic.cpp
#include
#include
#include
#include
std::mutex mutex_;
std::condition_variable condVar;
std::atomic dataReady{false};
void waitingForWork(){
std::cout << "Waiting " << std::endl;
std::unique_lock<:mutex> lck(mutex_);
condVar.wait(lck, []{ return dataReady.load(); });? // (1)
std::cout << "Running " << std::endl;
}
void setDataReady(){
dataReady = true;
std::cout << "Data prepared" << 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;
}
與第一個版本相比,該程序非常簡單,因為dataReady不必由互斥量保護。程序再次處于競爭狀態,可能導致死鎖。為什么?dataReady是原子的!是的,但是第(1)行中的wait表達式(condVar.wait(lck,[] {return dataReady.load();});)比看起來復雜得多。
wait表達式等效于以下四行:
std :: unique_lock < std ::互斥> lck(mutex_);
while(! [] { return dataReady.load();}(){
//時間窗口(1)? ? condVar.wait(lck);}
即使將dataReady設為原子,也必須在互斥鎖下對其進行修改;如果不是,則可能會發布對等待線程的修改,但不能正確同步。這種競爭狀況可能會導致死鎖。這是什么意思:已發布但未正確同步。讓我們仔細看一下前面的代碼片段,并假設數據是原子的,并且不受互斥對象Mutex_的保護。
讓我假設在條件變量condVar在等待表達式中但不在等待狀態時發送通知。這意味著線程的執行位于注釋時間窗口(第1行)所在行的源代碼片段中。結果是通知丟失。之后,線程返回等待狀態,并且可能永遠休眠。
如果dataReady受互斥鎖保護,則不會發生這種情況。由于與互斥鎖同步,因此僅在條件變量(因此接收方線程)處于等待狀態時才發送通知。
多么恐怖的故事?有沒有可能使用開始的程序conditionVariables.cpp更容易?不,不是帶有條件變量的,但是您可以使用promise和future配對來使工作完成
總結
以上是生活随笔為你收集整理的php 语法 条件变量,C ++核心准则:注意条件变量的陷阱的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何实现php自动备份数据库,使用php
- 下一篇: php动态网页转换成html,怎么把动态