one last boost conversion question: notify_one
I have a message queue class that has the following API: post( msg ); msg get(); where get( msg ) blocks until there is a message available and post( msg ) notifies when a message is posted. There are four threads waiting in get: MSG mqueue::get(); { MSG msgResult; { boost::mutex::scoped_lock lockQueue( _mutexQueue ); _conditionMessageReady.wait( lockQueue ); if ( _queueOut.size() > 0 ) { msgResult = _queueOut.front(); _queueOut.pop(); } } return msgResult; } The post method USED TO do this: void mqueue::post( MSG msg ) { { boost::mutex::scoped_lock lockQueue( _mutexQueue ); _queueOut.push(msg); } _conditionMessageReady.notify_one(); } When the queue was just a little bit busy (one thread processing, not all four), messages would go into the queue and never come out. I changed the .notify_one() to .notify_all() and I no longer have messages getting stuck in the queue. However, now I have a bunch of threads getting released when there is nothing for them to do (they just loop around and wait again, but YUCK!). Am I missing something or is this "expected" behavior? Thanks again, - Mark P.S. The lockQueue is locked when the condition is done waiting, right?
Mark Sizer wrote:
I have a message queue class that has the following API:
post( msg ); msg get();
[...]
Am I missing something or is this "expected" behavior?
Yes, it seems that you're missing something. http://www.boost.org/libs/thread/example/condition.cpp But note that you don't really need to own the mutex ["predictable scheduling" aside] while signaling. Also, certain optimizations are possible for single-consumer and/or single-producer vs. the classic bounded buffer with multiple consumers/producers. Yeah, and you'll NEVER beat Ada's "proxy model"-monitors ("protected objects"). ;-) regards, alexander.
Mark, The problem is that your "get" waits on the condition before it checks if the condition has already been met. So if you call "get" after the producer has already "post"ed, you'll sit on the condition forever. The canonical form of waiting on a condition looks like this:
MSG mqueue::get(); { MSG msgResult; { boost::mutex::scoped_lock lockQueue( _mutexQueue ); while ( _queueOut.empty() ) _conditionMessageReady.wait( lockQueue );
msgResult = _queueOut.front(); _queueOut.pop(); } return msgResult; }
Now if you call get and there's something in the queue, there's no need to wait on the condition.
P.S. The lockQueue is locked when the condition is done waiting, right?
Yes, the lock is automatically released when entering wait, and acquired before exiting it. Hope this helps! Christopher
Again, duh on me. Thank you. I don't want to use the 'while' paradigm, because I want to be able to free all the blocked threads on shutdown so they all gracefully terminate (destructors called, etc...). Getting a default constructed message back is OK as long as it's rare. I've refactored to this (it seems to work, but I'm posting it anyway): MSG mqueue::get() { MSG msgResult; { boost::mutex::scoped_lock lockQueue( _mutexQueue ); if ( _queueOut.empty() ) { _conditionMessageReady.wait( lockQueue ); } if ( !_queueOut.empty() ) { msgResult = _queueOut.front(); _queueOut.pop(); } else { // this happens after a flush() call (on shutdown) // (and the occasional race condition) } } return msgResult; } ( and changed post back to .notify_one() ) Thanks again, - Mark Christopher Currie wrote:
Mark,
The problem is that your "get" waits on the condition before it checks if the condition has already been met. So if you call "get" after the producer has already "post"ed, you'll sit on the condition forever. The canonical form of waiting on a condition looks like this:
MSG mqueue::get(); { MSG msgResult; { boost::mutex::scoped_lock lockQueue( _mutexQueue ); while ( _queueOut.empty() )
_conditionMessageReady.wait( lockQueue );
msgResult = _queueOut.front();
_queueOut.pop(); } return msgResult; }
Now if you call get and there's something in the queue, there's no need to wait on the condition.
P.S. The lockQueue is locked when the condition is done waiting, right?
Yes, the lock is automatically released when entering wait, and acquired before exiting it.
Hope this helps!
Christopher
Info: http://www.boost.org Wiki: http://www.crystalclearsoftware.com/cgi-bin/boost_wiki/wiki.pl Unsubscribe: mailto:boost-users-unsubscribe@yahoogroups.com
Your use of Yahoo! Groups is subject to http://docs.yahoo.com/info/terms/
participants (3)
-
Alexander Terekhov
-
Christopher Currie
-
Mark Sizer