条件変数の詳細理解のまとめ(C++11のコンカレントライブラリを例に)

7971 ワード

条件変数の理解
条件変数はthread間の通信を管理するために使用できます.スレッドは、イベントが発生するまで条件変数で待機できます.
シーンを考慮すると、スレッドがキューにアクセスすると、キューが空であり、他のスレッドがノードをキューに追加するまで待つしかありません.この場合、条件変数を使用する必要があります.(もちろん、 でポーリングしてもいいし、キューにノードがあるかどうかを判断し続けます)
C++11標準ライブラリ提供のcondition_variableの概要
condition_variable cv{};  //      
cv.~condition_variable();  //     

----------
cv.notify_one();   //                
cv.notify_all();    //                 
----------

cv.wait(lck);           //             (      )
cv.wait(lck,pred);      //     while(!pared())wait(lck)
x=cv.wait_util(lck,tp);   //                   
                        //    x        x==timeout,  x==cv_status::no_timeou

b=cv.wait_until(lck,tp,pred) //    : while(!pared())
                                 //     { if(wait_until(lck,tp)==cv_sattus::timeout);}b=pred()

x=cv.wait_for(lck,d);   //               @d       
b=cv.wait_for(lck,d,pred);  //    b=cv.wait_until(lck,steady_clock::now()+d,std::move(pre)); 


プロファイリング待機アクションcv.wait(lck)は、通常、1つの反発ロックと組み合わせてコードを使用する必要がある.
//     
...
condition   cond;    //      
mutex   mu;           //    
/*
        
*/
mu.lock();
while(v.is_empty())
{
  cond.wait(&mu);
}
 ...do something  .. 
mu.unlock();
/*
      
*/

waitを呼び出すとき:まず、vを保護するロックを取得する必要があります.vは、複数のスレッドがアクセスできるはずです.wait関数では、1.wait関数の内部はまずmu.unlock()ロックを解除する;2.そして待機に入る.3.起動するとmuが呼び出される.lock()は再びロックを取得します.
なぜwait関数は本当に待つ前にロックを解放するのですか?なぜなら:1.変数vは、vという条件を変更できるように、他のスレッドがロックを取得し、v.is_empty()に要素を追加する必要がある.2.他のスレッドも臨界領域に入る機会を与えて同じ条件を待つ
生産者-消費者サンプルコード
//sync_queue 

#include 
#include 
#include 
#include 
#include  
#include 

template<typename T>
class Sync_queue
{
public:
    Sync_queue();
    ~Sync_queue();

    void put(const T& val);
    void put(T&& val);
    void get(T& val);
private:
    std::condition_variable m_cond;
    std::mutex   m_mutex;
    std::list m_q;
};

template<typename T>
Sync_queue::~Sync_queue()
{

}

template<typename T>
void Sync_queue::get(T& val)
{
    std::unique_lock<std::mutex> lck(m_mutex);
    while (m_q.empty())     //  while     if             
    {
        if (m_cond.wait_for(lck, std::chrono::milliseconds{ 2000 }) == std::cv_status::timeout)
        {
            break;
        }
    }
    if (m_q.empty())
    {
        throw "  ";
    }
    else
    {
        val = m_q.front();
        m_q.pop_front();

        std::cout << "      
"
; } } template<typename T> Sync_queue::Sync_queue() { } template<typename T> void Sync_queue::put(T&& val) { std::lock_guard<std::mutex> lck(m_mutex); m_q.push_back(std::forward(val)); m_cond.notify_one(); } template<typename T> void Sync_queue::put(const T& val) { { std::lock_guard<std::mutex> lck(m_mutex); m_q.push_back(val); } m_cond.notify_one(); // mutex } //main.cpp #include "Sync_queue.h" Sync_queue<int> g_queue; void produce() { static int i = 0; while (true) { g_queue.put(++i); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } } void consumer() { while (true) { try { int val; g_queue.get(val); std::cout << " :" << val << std::endl; } catch (const char * e) { std::cout << e << std::endl; } } } int main() { std::thread t_c{ consumer }; std::thread t_p{ produce }; t_c.join(); t_p.join(); return 0; } // put // if(m_q.size()>n) // {m_cond.notify_all();} // else // {m_cond.notify_one();}

まとめ:主にwait内部で何をしたのか、waitに伝わる反発ロックを理解することです.