別のスレッドがタスクキューの処理を完了するまでスレッドを停止するために条件変数を使用しています(長い話)。したがって、1つのスレッドでロックして待機します。
boost::mutex::scoped_lock lock(m_mutex);
m_condition.wait(lock);
他のスレッドがタスクを完了すると、次のように待機中のスレッドに通知します。
boost::mutex::scoped_lock lock(m_parent.m_mutex);
m_parent.m_condition.notify_one();
私が見ている問題は、それに続く命令にブレークポイントを設定しない限り、待機スレッドが待機を停止しないことです(xcode、fyiを使用しています)。はい、これは奇妙に思えます。なぜこれが起こっているのか誰にも分かりますか?条件変数を誤って使用していますか?
はい、条件変数を誤用しています。 「条件変数」は、実際には単なるシグナル伝達メカニズムです。また、条件をテストする必要があります。あなたの場合、notify_one()
を呼び出しているスレッドは、wait()
を呼び出しているスレッドが開始する前に実際に完了することがあります。 (または、少なくとも、notify_one()
呼び出しはwait()
呼び出しの前に行われます。)これは「逃されたウェイクアップ」と呼ばれます。
解決策は、気になる条件を含む変数を実際に持つことです:
_bool worker_is_done=false;
boost::mutex::scoped_lock lock(m_mutex);
while (!worker_is_done) m_condition.wait(lock);
_
そして
_boost::mutex::scoped_lock lock(m_mutex);
worker_is_done = true;
m_condition.notify_one();
_
他のスレッドが待機を開始する前の_worker_is_done==true
_の場合は、wait()
を呼び出さずにwhileループを抜けることができます。
このパターンは非常に一般的であるため、condition_variable.wait()
をラップするwhile
ループがない場合は、常にバグが発生します。実際、C++ 11がboost :: condtion_variableに似たものを採用したとき、述語ラムダ式をとる新しい種類のwait()を追加しました(本質的にwhile
ループを行います):
_std::condition_variable cv;
std::mutex m;
bool worker_is_done=false;
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return worker_is_done;});
_
議論に基づいて、ブースト条件の使用方法を示す例を実装しました。
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
boost::mutex io_mutex;
bool worker_is_done = false;
boost::condition_variable condition;
void workFunction()
{
std::cout << "Waiting a little..." << std::endl;
boost::this_thread::sleep(boost::posix_time::seconds(1));
worker_is_done = true;
std::cout << "Notifying condition..." << std::endl;
condition.notify_one();
std::cout << "Waiting a little more..." << std::endl;
boost::this_thread::sleep(boost::posix_time::seconds(1));
}
int main()
{
boost::mutex::scoped_lock lock(io_mutex);
boost::thread workThread(&workFunction);
while (!worker_is_done) condition.wait(lock);
std::cout << "Condition notified." << std::endl;
workThread.join();
std::cout << "Thread finished." << std::endl;
return 0;
}