boost-Bedingung variable Problem
Folgende minimale Codebeispiel in einem größeren Programm sendet Befehle aus client-threads auf einen asio-io_service-Objekt. Das io_service-Objekt (in der Ios-Klasse) wird mit einem Faden. Wenn der Befehl gesendet wird, der client-thread wartet bis er benachrichtigt wird, die von der Ios Objekt (via Cmd::NotifyFinish ()), dass es abgeschlossen ist.
Dieser Probe scheint zu laufen auf Linux Ubuntu 11.04 mit boost 1.46 gut, aber auf Windows 7 boost 1.46 behauptet.
Ich vermute, es ist etwas zu tun mit der lock-in Cmd::NotifyFinish(). Wenn ich den lock-out der verschachtelten Rahmen, so dass, wenn waitConditionVariable_.notify_one() aufgerufen, in der die Sperre der Rahmen ist es nicht Abstürzen auf Windows 7. Jedoch, boost::thread-Dokumentation besagt, dass notify_one() muss nicht aufgerufen werden, innerhalb der Schleuse.
Den stack-trace (unten) zeigt, es wird behauptet, wenn notify_one() aufgerufen wird. Es ist, als ob die cmd-Objekt verschwunden ist, bevor Benachrichtigen genannt wird...
Wie Mach ich das dem thread-sichere und nicht geltend machen?
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/condition_variable.hpp>
#include <boost/bind.hpp>
#include <iostream>
class Cmd
{
public:
Cmd() : cnt_(0), waitPred_(false), waiting_(false)
{
}
virtual ~Cmd()
{
}
void BindInfo(int CmdSeq)
{
cnt_ = CmdSeq;
}
void NotifyFinish()
{
//call by service thread...
{
boost::mutex::scoped_lock lock(waitMutex_);
waitPred_ = true;
if (!waiting_)
{
//don't need to notify as isn't waiting
return;
}
}
waitConditionVariable_.notify_one();
}
void Wait()
{
//called by worker threads
boost::mutex::scoped_lock lock(waitMutex_);
waiting_ = true;
while (!waitPred_)
waitConditionVariable_.wait(lock);
}
int cnt_;
private:
boost::mutex waitMutex_;
boost::condition_variable waitConditionVariable_;
bool waitPred_;
bool waiting_;
};
class Ios
{
public:
Ios() : timer_(ios_), cnt_(0), thread_(boost::bind(&Ios::Start, this))
{
}
void Start()
{
timer_.expires_from_now(boost::posix_time::seconds(5));
timer_.async_wait(boost::bind(&Ios::TimerHandler, this, _1));
ios_.run();
}
void RunCmd(Cmd& C)
{
ios_.post(boost::bind(&Ios::RunCmdAsyn, this, boost::ref(C)));
}
private:
void RunCmdAsyn(Cmd& C)
{
C.BindInfo(cnt_++);
C.NotifyFinish();
}
void TimerHandler(const boost::system::error_code& Ec)
{
if (!Ec)
{
std::cout << cnt_ << "\n";
timer_.expires_from_now(boost::posix_time::seconds(5));
timer_.async_wait(boost::bind(&Ios::TimerHandler, this, _1));
}
else
exit(0);
}
boost::asio::io_service ios_;
boost::asio::deadline_timer timer_;
int cnt_;
boost::thread thread_;
};
static Ios ios;
void ThreadFn()
{
while (1)
{
Cmd c;
ios.RunCmd(c);
c.Wait();
//std::cout << c.cnt_ << "\n";
}
}
int main()
{
std::cout << "Starting\n";
boost::thread_group threads;
const int num = 5;
for (int i = 0; i < num; i++)
{
//Worker threads
threads.create_thread(ThreadFn);
}
threads.join_all();
}
stack-trace
msvcp100d.dll!std::_Debug_message(const wchar_t * message, const wchar_t * file, unsigned int line) Line 15 C++
iosthread.exe!std::_Vector_const_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > >::_Compat(const std::_Vector_const_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > & _Right) Line 238 + 0x17 bytes C++
iosthread.exe!std::_Vector_const_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > >::operator==(const std::_Vector_const_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > & _Right) Line 203 C++
iosthread.exe!std::_Vector_const_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > >::operator!=(const std::_Vector_const_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > & _Right) Line 208 + 0xc bytes C++
iosthread.exe!std::_Debug_range2<std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > >(std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _First, std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _Last, const wchar_t * _File, unsigned int _Line, std::random_access_iterator_tag __formal) Line 715 + 0xc bytes C++
iosthread.exe!std::_Debug_range<std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > >(std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _First, std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _Last, const wchar_t * _File, unsigned int _Line) Line 728 + 0x6c bytes C++
iosthread.exe!std::find_if<std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > >,bool (__cdecl*)(boost::intrusive_ptr<boost::detail::basic_cv_list_entry> const &)>(std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _First, std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _Last, bool (const boost::intrusive_ptr<boost::detail::basic_cv_list_entry> &)* _Pred) Line 92 + 0x54 bytes C++
iosthread.exe!std::remove_if<std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > >,bool (__cdecl*)(boost::intrusive_ptr<boost::detail::basic_cv_list_entry> const &)>(std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _First, std::_Vector_iterator<std::_Vector_val<boost::intrusive_ptr<boost::detail::basic_cv_list_entry>,std::allocator<boost::intrusive_ptr<boost::detail::basic_cv_list_entry> > > > _Last, bool (const boost::intrusive_ptr<boost::detail::basic_cv_list_entry> &)* _Pred) Line 1848 + 0x58 bytes C++
iosthread.exe!boost::detail::basic_condition_variable::notify_one() Line 267 + 0xb4 bytes C++
iosthread.exe!Cmd::NotifyFinish() Line 41 C++
- +1 für den Vervielfältiger
Du musst angemeldet sein, um einen Kommentar abzugeben.
Das problem ist, dass die bedingungsvariable ist ein Mitglied der
Cmd
Objekt, das erstellt wird, durch den client-thread und zerstört, die client-thread, wenn das warten beendet ist.So haben Sie eine race-Bedingung, wenn:
boost::condition_variable::notify_one()
wird aufgerufen, auf dem "service-thread"notify_one
.So Ihre Beobachtung, dass es "als ob die
cmd
Objekt verschwunden ist, bevor Benachrichtigen" genannt wird, ist so ziemlich genau das, was passiert ist, denke ich. Außer, dass dieCmd
Objekt nicht verschwinden, bevornotify_one()
heißt, es verschwand, währendnotify_one()
war seine Arbeit. Deinen anderen Hinweis, dass "dieboost::thread
Dokumentation besagt, dassnotify_one()
braucht nicht genannt zu werden, innerhalb der Schleuse" ist wahr, aber das bedeutet nicht, dass die condition-variable kann zerstört werden, bevornotify_one()
zurückgegeben hat.Benötigen Sie zum verwalten der Lebensdauer der
Cmd
Objekt, so dass der service-thread getan, es zu benutzen, bevor es zerstört wird - halten Sie den mutex, die in derCmd
Objekt, währendnotify_one()
genannt wird, ist eine Möglichkeit, das zu tun (wie Sie bemerkt haben). Oder Sie können ziehen Sie den condition-variable aus derCmd
Objekt, so dass seine Lebensdauer ist unabhängig von derCmd
Objekt (vielleichtshared_ptr<>
können).Beachten Sie auch, dass ich glaube, dass die
waiting_
Mitglied derCmd
Klasse ist überflüssig - Sie können rufen Sienotify_one()
odernotify_all()
wenn es keine Kellner, die auf eine Zustand-variable - es ist bereits tun, die Prüfung für das für Sie (ich glaube nicht, dass es schadet nichts, nur dass es die Komplexität, das muss nicht sein, in derCmd
Klasse).NotifyFinish()
hält die mutex, während es ruftnotify_one()
, dann in Ihrem ersten Szenario, woNotifyFinish()
wird zuerst angerufen, werde es packen der mutex gesetztwaitPred_ = true
sind, dann rufen Sienotify_one()
(die nichts tun). All das wird geschehen, bevorWait()
eine chance bekommt, etwas zu tun, denn wenn er es versucht werden blockiert, wartet auf den mutex erwerben. EinmalNotifyFinish()
Versionen der mutex, derWait()
Anruf wird in der Lage sein zu gehen; Sie werden bemerken, dasswaitPred_
isttrue
und nicht die Mühe aufrufencondition_variable::wait()
.Da diese Schleife ist unendlich, warum nicht einfach Cmd-c; außerhalb der while(1) so, dass es wiederverwendet 'c' in jeder iteration der while(1) ?