how to wait for a thread until it stopped
Hi there, I'm still working on the transistion from an old thread lib to the boost thread lib. I have two threads, the GUI thread and a worker thread. The worker thread is always inside a loop that only breaks if someone set the flag. The GUI thread has from time to time to stop the worker thread. This is currently done by setting a flag ( _bStop ) and the worker thread will eventually stop like: void run() { boost::mutex::scoped_lock oLock( _oRunMutex ); do { ... if( _bStop ) _oRunCondition.wait( oLock ); } while( _bTerminate == false ); } I believe that this implemention can lead to deadlocks. But how can I implement the behavior that the GUI thread will break until the worker thread has stopped. Thanks, Christian
Christian Henning wrote:
Hi there, I'm still working on the transistion from an old thread lib to the boost thread lib.
I have two threads, the GUI thread and a worker thread. The worker thread is always inside a loop that only breaks if someone set the flag.
The GUI thread has from time to time to stop the worker thread. This is currently done by setting a flag ( _bStop ) and the worker thread will eventually stop like:
void run() { boost::mutex::scoped_lock oLock( _oRunMutex );
do { ...
if( _bStop ) _oRunCondition.wait( oLock );
} while( _bTerminate == false ); }
You must use "while" rather than "if" to test the condition associated with a condition variable because of the possibility of spurious wake-ups (and in some cases the possibility of the condition becoming false by the time the thread reacquires the mutex). Also you should not use an initial underscore in the names of global variables. It's not clear from this code whether you're using such names for global or class member variables.
I believe that this implemention can lead to deadlocks. But how can I implement the behavior that the GUI thread will break until the worker thread has stopped.
Use the thread::join function. Ben.
Thanks for the answer. Here is more better code sample. The _oThread object is RAII object that makes sure the boost::thread::join() is called when the object is destructed. fvAsynchronousSequencer::fvAsynchronousSequencer() F_CE( throw( fException ) ) : _oThread ( ) , _bTerminateSequencerThread ( false ) , _bStop( false ) { _oThread.create( this, _run ); } // Only called by GUI thread fvAsynchronousSequencer::~fvAsynchronousSequencer() throw() { _bTerminateSequencerThread = true; // wake up the thread, if necessary _oRunCondition.notify_one(); // wait until the thread is terminated { boost::mutex::scoped_lock oLock( _oTerminateMutex ); _oTerminateCond.wait( oLock ); } } // Only called by GUI thread void fvAsynchronousSequencer::start() { _bStop = false; _oRunCondition.notify_one(); } // Only called by GUI thread void fvAsynchronousSequencer::stop() throw() { _bStop = true; // I would like to wait here until the _oThread is a in wait position. } void fvAsynchronousSequencer::_run() throw() { boost::mutex::scoped_lock oLock( _oRunMutex ); // Wait until someone calls start() _oRunCondition.wait( oLock ); do { while( _bStop ) _oRunCondition.wait( oLock ); } while( !_bTerminateSequencerThread ); // now finish destructing the sequencer _oTerminateCond.notify_one(); } Thanks is advance, Christian
Hi Ben, The boost::thread::join() is only for waiting for a thread to complete. I don't want to complete the thread when calling stop(). I just want to put it into the blocked state. I run into a deadlock when I finish the app. I call the stop() and then the ~fvAsynchronousSequencer. But usually the thread isn't stopped when entering the destructor and when the thread stops after I called notify_one() I'm inside a deadlock. So, is there a way, that when the stop() returns, the worker thread is blocked? Thanks, Christian
Christian Henning wrote:
Hi Ben, The boost::thread::join() is only for waiting for a thread to complete. I don't want to complete the thread when calling stop(). I just want to put it into the blocked state.
I run into a deadlock when I finish the app. I call the stop() and then the ~fvAsynchronousSequencer. But usually the thread isn't stopped when entering the destructor and when the thread stops after I called notify_one() I'm inside a deadlock.
Looks like you've missed what Ben said -- you should never, ever use condition variable in the way you do: { boost::mutex::scoped_lock oLock( _oTerminateMutex ); _oTerminateCond.wait( oLock ); } This is asking for troubles -- you can miss "notify_one" if it's executed before you call "wait", and you get awake when nobody called "notify_one". Some more details can be found in: http://vladimir_prus.blogspot.com/2005/07/spurious-wakeups.html and any book on threading says the same.
So, is there a way, that when the stop() returns, the worker thread is blocked?
Sure: enum Worker_status { running = 1, stopped } status; void stop() { boost::mutex::scoped_lock l(m); _bStop = true; while(status != stopped) { cond.wait(l); } _bStop = false; } void runner() { for(;;) { // so some work. if (_bStop) { boost::mutex::scope_lock l(m); status = stopped; cond.notify_all(); } } } or something like that. I haven't seen your header -- but making _bStop volatile will not hurt too. - Volodya
Thank you very much for the useful information. I think I have to learn a lot more about the Posix threads. This is now my Sequencer and I believe it is running correctly. But of course, every feedback is welcome: class Sequencer : private boost::noncopyable { public: Sequencer(); ~Sequencer(); void start(); void stop(); private: enum teThreadState { _NOT_STARTED_ = 1, _RUNNING_ , _STOPPED_ }; private: void _run(); private: teThreadState volatile _eThreadState; bool volatile _bStarted; boost::mutex _oRunMutex; boost::condition _oRunCond; bool volatile _bStop; boost::mutex _oStopMutex; boost::condition _oStopCond; bool volatile _bTerminated; bool volatile _bTerminateSequencer; boost::mutex _oTerminateMutex; boost::condition _oTerminateCond; fActive<Sequencer> _oThread; }; Sequencer::Sequencer() : _oThread() , _bStarted ( false ) , _bStop ( false ) , _bTerminateSequencer( false ) , _bTerminated ( false ) , _eThreadState( _NOT_STARTED_ ) { _oThread.create( this, _run ); } Sequencer::~Sequencer() { // signal the thread to terminate _bTerminateSequencer = true; // wake up the thread if it's sleeping _oRunCond.notify_one(); // wait until the thread is terminated { boost::mutex::scoped_lock oLock( _oTerminateMutex ); while( !_bTerminated ) _oTerminateCond.wait( oLock ); } } void Sequencer::start() { _bStarted = true; _bStop = false; _oRunCond.notify_one(); } void Sequencer::stop() { boost::mutex::scoped_lock oLock ( _oStopMutex ); _bStop = true; while( _eThreadState != _STOPPED_ ) _oStopCond.wait( oLock ); } void Sequencer::_run() { boost::mutex::scoped_lock oLock( _oRunMutex ); //wait until start() was called while( !_bStarted ) _oRunCond.wait( oLock ); _eThreadState = _RUNNING_; do { if( _bStop ) { _eThreadState = _STOPPED_; _oStopCond.notify_one(); while( _bStop ) _oRunCond.wait( oLock ); } _eThreadState = _RUNNING_; } while( !_bTerminateSequencer ); _bTerminated = true; _oTerminateCond.notify_one(); return; } If someone is interessted in the fActive template class, just let me know. I think I should also put the thread inside the fActive class. Thanks so much again, for the tips, Christian
On Thu, Aug 11, 2005 at 01:24:08PM -0400, Christian Henning wrote:
Thank you very much for the useful information. I think I have to learn a lot more about the Posix threads.
i usually make a join on the thread i want to wait for it to exit. of course my threads are joinable :) cheers domenico -----[ Domenico Andreoli, aka cavok --[ http://people.debian.org/~cavok/gpgkey.asc ---[ 3A0F 2F80 F79C 678A 8936 4FEE 0677 9033 A20E BC50
On 8/11/05, Domenico Andreoli
On Thu, Aug 11, 2005 at 01:24:08PM -0400, Christian Henning wrote:
Thank you very much for the useful information. I think I have to learn a lot more about the Posix threads.
i usually make a join on the thread i want to wait for it to exit. of course my threads are joinable :)
That is being done in the fActive class destructor. The fActive is RAII class. Here is the fActive class: template<class _T_> class fActive : private boost::noncopyable { public: typedef void (_T_::*tfpActiveMethod)() throw(); public: fActive() throw(); ~fActive() throw(); // // Thread creation // void create( _T_ * pActiveObject, tfpActiveMethod pActiveMethod ) throw( fInvalidParameterException, fStandardException ); private: // we need a pointer since we contruct the thread inside the create() // and there is no assignment allowed for thread objects boost::thread* _pThread; _T_ * _pActiveObject; tfpActiveMethod _pActiveMethod; bool _bCreated; }; //----------------------------------------------------------------------------- // fActive inline methods // template<class _T_> fActive<_T_>::fActive() F_CE( throw() ) : _pThread ( NULL ) , _pActiveObject( NULL ) , _pActiveMethod( NULL ) , _bCreated( false ) { ; } template<class _T_> fActive<_T_>::~fActive() throw() { if( _bCreated ) { _pThread->join(); delete _pThread; _pThread = NULL; } } template<class _T_> void fActive<_T_>::create( _T_ * pActiveObject , tfpActiveMethod pActiveMethod ) throw( fInvalidParameterException, fStandardException ) { F_ASSERT_PARAM( pActiveObject != NULL ); F_ASSERT_PARAM( pActiveMethod != NULL ); _pActiveObject = pActiveObject; _pActiveMethod = pActiveMethod; try { _pThread = new boost::thread( boost::bind( _pActiveMethod , _pActiveObject )); } catch( std::exception ex ) { fStandardException oNewException( ex, "Creating thread in active object" ); F_THROW( oNewException ); } _bCreated = true; } Greets, Christian
participants (4)
-
Ben Hutchings
-
Christian Henning
-
Domenico Andreoli
-
Vladimir Prus