簡體   English   中英

std :: condition_variable-等待多個線程通知觀察者

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM