![](/img/trans.png)
[英]std::condition_variable wait() and notify_one() synchronization
[英]std::condition_variable - Wait for several threads to notify observer
我的問題是這樣的:
我有一個觀察者,它持有一個std :: condition_variable和一個std :: mutex,我的工作線程對象有一個指向觀察者的指針。 每當工作線程完成其工作時,它都會調用m_Observer-> NotifyOne(),然后再調用condition_variable的notify_one()函數。 現在我想做的是,啟動一堆工作線程,每個工作線程都有不同的工作和不同的(獨立的)數據,並等待所有工作線程向觀察者發出信號(使用m_Observer-> NotifyOne()),以便我能夠根據所有輔助線程的結果繼續工作。
我的觀察者看起來像這樣:
class IAsyncObserver
{
private:
std::condition_variable m_ObserverCV;
bool m_bNotified;
std::mutex m_Mutex;
public:
IAsyncObserver()
{
m_bNotified = false;
}
~IAsyncObserver()
{
/*m_bNotified = true;
m_ObserverCV.notify_all();*/
}
void NotifyOne()
{
std::unique_lock<std::mutex> Lock(m_Mutex);
m_bNotified = true;
m_ObserverCV.notify_one();
}
void NotifyAll()
{
std::unique_lock<std::mutex> Lock(m_Mutex);
m_bNotified = true;
m_ObserverCV.notify_all();
}
void WaitForNotifications(uint32_t _uNumOfNotifications = 1)
{
uint32_t uNotifyCount = 0;
while (uNotifyCount < _uNumOfNotifications)
{
std::unique_lock<std::mutex> Lock(m_Mutex);
m_bNotified = false;
m_ObserverCV.wait(Lock);
if (m_bNotified)
++uNotifyCount;
}
}
}; // IAsyncObserver
_uNumOfNotifications是我要等待的工作線程數。
現在,每個工作線程都應該運行一個模擬功能,該功能在一個時間步/數據垃圾中執行實際工作,然后暫停/等待直到觀察者通知工作人員繼續。
一個工作程序的線程函數可能看起來像這樣:
do{
//suspend simulation
while (m_PauseSimulation.load())
{
std::unique_lock<std::mutex> wait(m_WaitMutex);
m_CV.wait(wait);
if (m_RunSimulation.load() == false)
{
SignalObserver();
return;
}
}
//lock the data while simulating
{
std::lock_guard<std::mutex> lock(m_LockMutex);
//update simulation
Simulate(static_cast<float>(m_fDeltaTime));
m_PauseSimulation.store(true);
}
//notify the physics manager that work is done here
SignalObserver();
} while (m_RunSimulation.load());
SignalObserver()僅調用m_Observer-> NotifyOne()。
現在的問題是,一段時間后,線程在某個地方陷入死鎖,觀察者沒有通知他們繼續下一步。 問題可能在WaitForNotifications()函數中的某處,但是我不確定。 Atm我只有一個工作線程,所以uNumOfNotifications = 1,但是仍然會遇到在m_ObserverCV.wait(Lock)和m_CV.wait(wait)等待的問題,我什至不確定它是否真的是一個死鎖或其他東西使用condition_variable,因為我嘗試從多個線程訪問它。
在這一點上,我想引用內德·佛蘭德斯(Ned Flanders)的父親:“我們什么也沒嘗試,而且全都沒有想法!”
謝謝你的幫助。 永遠的小費表示贊賞。
法比安
編輯:
感謝所有有用的信息和建議。 由於沒有找到有關std :: barriers的任何信息,我最終實現了Michael的第二個想法。 所以這就是我所做的:
class IAsyncObserver
{
private:
std::condition_variable m_ObserverCV;
bool m_bNotified;
std::mutex m_Mutex;
uint32_t m_uNumOfNotifications;
uint32_t m_uNotificationCount;
public:
IAsyncObserver()
{
m_bNotified = false;
m_uNumOfNotifications = 0;
m_uNotificationCount = 0;
}
~IAsyncObserver()
{
/*m_bNotified = true;
m_ObserverCV.notify_all();*/
}
void SetBarrier(uint32_t _uNumOfNotifications = 1)
{
m_uNumOfNotifications = _uNumOfNotifications;
}
void NotifyBarrier()
{
std::unique_lock<std::mutex> Lock(m_Mutex);
if (++m_uNotificationCount >= m_uNumOfNotifications)
{
m_bNotified = true;
m_ObserverCV.notify_one();
}
}
void WaitForNotifications()
{
std::unique_lock<std::mutex> Lock(m_Mutex);
while (m_bNotified == false)
{
m_ObserverCV.wait(Lock);
}
m_uNotificationCount = 0;
}
void NotifyOne()
{
std::unique_lock<std::mutex> Lock(m_Mutex);
m_bNotified = true;
m_ObserverCV.notify_one();
}
void NotifyAll()
{
std::unique_lock<std::mutex> Lock(m_Mutex);
m_bNotified = true;
m_ObserverCV.notify_all();
}
}; // IAsyncObserver
在我的“主要”功能中:MassSpringSystem和RigidBodySystem是我的工作人員atm
//update systems here:
{
SetBarrier(m_uTotalNotifyCount);
{ //start MassSpringSystems
std::lock_guard<std::mutex> lock(m_LockMutex);
for (std::shared_ptr<MassSpringSystem> MSS : m_MassSpringSystems)
{
MSS->SetDeltaTime(fDeltaTime);
MSS->Continue();
}
}
//ATTENTION this system works directly on the m_OctreeEntities!
{ //start RigidBodySystems
m_RigidBodySystem.SetDeltaTime(fDeltaTime);
m_RigidBodySystem.AddData(m_RigidBodies);
m_RigidBodySystem.Continue();
}
//wait for all systems to finish -> till they call SignalObserver
WaitForNotifications();
}
和上面一樣,在worker的線程函數中,但是這次SignalObserver調用NotifyBarrier()
現在一切正常。 一個簡單而強大的解決方案,謝謝!
您嘗試以不希望使用它們的方式使用條件變量-在這種情況下,您假定可以對通知進行計數。 你不能 這樣一來,您可能會丟失通知,並且您正在計算標准允許的虛假喚醒。
取而代之的是,您應該使用在互斥下增加的計數器,並且僅在計數器達到工作線程數時才用信號通知條件變量。 (最后在每個工作人員中執行此操作)。 主線程一直在條件變量上休眠,直到計數器達到期望值為止。 (當然,計數器的驗證也必須包含用於遞增的互斥量)。 據我所知,用原子替換互斥的計數器(不對其進行互斥)似乎是不可能的,因為您不能原子地檢查計數器並在condvar上休眠,因此您將獲得競爭條件而無需使計數器互斥。
Boost線程已知的另一個同步原語是障礙,它沒有進入C ++ 11。 您構造一個屏障,並將其傳遞給工作線程的數量加上一個作為構造函數參數的屏障。 所有工作線程都應在其末端等待條件變量,而主線程應在構造工作線程之后等待。 所有線程將在該屏障上阻塞,直到所有工作線程和主線程都阻塞為止,並在那一刻被釋放。 因此,如果釋放了主線程,您就知道所有工作人員都已完成。 但是,這有一個問題:沒有工作線程完成(並釋放關聯的管理資源),直到所有工作線程都完成為止,這對您可能是或不是問題。 這個問題提出了使用C ++ 11線程工具實現boost :: barrier的實現。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.