条件変数の詳細理解のまとめ(C++11のコンカレントライブラリを例に)
7971 ワード
条件変数の理解
条件変数はthread間の通信を管理するために使用できます.スレッドは、イベントが発生するまで条件変数で待機できます.
シーンを考慮すると、スレッドがキューにアクセスすると、キューが空であり、他のスレッドがノードをキューに追加するまで待つしかありません.この場合、条件変数を使用する必要があります.(もちろん、
C++11標準ライブラリ提供のcondition_variableの概要
プロファイリング待機アクション
waitを呼び出すとき:まず、
なぜwait関数は本当に待つ前にロックを解放するのですか?なぜなら:1.変数
生産者-消費者サンプルコード
まとめ:主にwait内部で何をしたのか、waitに伝わる反発ロックを理解することです.
条件変数は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に伝わる反発ロックを理解することです.