|
Boost.Threadscondition |
Introduction
Header
Synopsis
Members
Example
An object of class condition
is a synchronization
primitive used to cause a thread to wait until a particular shared-data
condition (or time) is met. A condition
object is always
used in conjunction with a mutex object modeling a Mutex Concept. The mutex must be locked prior
to waiting on the condition
, which is ensured by passing a
lock object modeling a Lock Concept to
the condition
object's wait
functions.
While the thread is waiting on the condition
object, the
mutex associated with the lock is unlocked. When the thread returns
from a call to one of the condition
object's
wait
functions, the mutex is again locked. The tricky
lock/unlock/lock sequence is performed automatically by the
condition
object's wait
functions.
The condition
type is often used to implement the
Monitor Object and other important patterns. See [Schmidt-00] and [Hoare 74]. Monitors are one of the
most important patterns for creating reliable multithreaded
programs.
See Formal Definitions for definitions of thread states blocked and ready. Note that "waiting" is a synonym for blocked.
#include <boost/thread/condition.hpp>
namespace boost { class condition : private boost::noncopyable // Exposition only. // Class condition meets the NonCopyable requirement. { public: condition(); ~condition(); void notify_one(); void notify_all(); template <typename ScopedLock> void wait(ScopedLock& lock); template <typename ScopedLock, typename Predicate> void wait(ScopedLock& lock, Predicate pred); template <typename ScopedLock> bool timed_wait(ScopedLock& lock, const xtime& xt); template <typename ScopedLock, typename Predicate> bool timed_wait(ScopedLock& lock, const xtime& xt, Predicate pred); }; } // namespace boost
condition();
Effects: Constructs a condition
.
~condition();
Effects: Destroys *this
.
void notify_one();
Effects: If there is a thread waiting on *this
,
change that thread's state to ready. Otherwise there is no
effect.
Notes: If more that one thread is waiting on the condition, it is unspecified which is made ready.
void notify_all();
Effects: Change the state of all threads waiting on
*this
to ready. If there are no waiting threads,
notify_all()
has no effect.
template <typename ScopedLock> void wait(ScopedLock& lock);
Requires: ScopedLock meets the ScopedLock requirements.
Effects: Releases the lock on the mutex model associated with lock
,
blocks the current thread of execution until readied by a call to
this->notify_one()
or
this->notify_all()
, and then reacquires the lock. All effects
occur in an atomic fashion.
Throws: lock_error
if !lock.locked()
Danger: This version should always be used within a loop
checking that the state logically associated with the
condition
has become true. Without the loop, race conditions can
ensue due to possible "spurious wake ups". The second version
encapsulates this loop idiom internally and is generally the preferred
method.
template <typename ScopedLock, typename Pr> void wait(ScopedLock& lock, Pr pred);
Requires: ScopedLock meets the ScopedLock requirements, return from
pred()
convertible to bool.
Effects: As if:
while (!pred()) wait(lock)
Throws: lock_error
if !lock.locked()
template <typename ScopedLock> bool timed_wait(ScopedLock& lock, const xtime& xt);
Requires: ScopedLock meets the ScopedLock requirements.
Effects: Releases the lock on the mutex model associated with the
lock
, blocks the current thread of execution until readied by a
call to this->notify_one()
or
this->notify_all()
, or until xt
, and then
reacquires the lock. All effects occur in an atomic fashion.
Throws: lock_error
if !lock.locked()
Danger: This version should always be used within a loop
checking that the state logically associated with the
condition
has become true. Without the loop, race conditions can
ensue due to "spurious wake ups". The second version
encapsulates this loop idiom internally and is generally the preferred
method.
Returns: false
if xt
is reached,
otherwise true
.
template <typename ScopedLock, typename Pr> bool timed_wait(ScopedLock& lock, const xtime& xt, Pr pred);
Requires: ScopedLock meets the ScopedLock requirements,
return from pred()
convertible to bool.
Effects: As if:
while (!pred())
{
if (!timed_wait(lock, xt))
return
false;
}
Throws: lock_error
if !lock.locked()
Returns: false
if xt
is reached,
otherwise true
.
#include <iostream> #include <vector> #include <boost/utility.hpp> #include <boost/thread/condition.hpp> #include <boost/thread/thread.hpp> class bounded_buffer : private boost::noncopyable { public: typedef boost::mutex::scoped_lock lock; bounded_buffer(int n) : begin(0), end(0), buffered(0), circular_buf(n) { } void send (int m) { lock lk(monitor); while (buffered == circular_buf.size()) buffer_not_full.wait(lk); circular_buf[end] = m; end = (end+1) % circular_buf.size(); ++buffered; buffer_not_empty.notify_one(); } int receive() { lock lk(monitor); while (buffered == 0) buffer_not_empty.wait(lk); int i = circular_buf[begin]; begin = (begin+1) % circular_buf.size(); --buffered; buffer_not_full.notify_one(); return i; } private: int begin, end, buffered; std::vector<int> circular_buf; boost::condition buffer_not_full, buffer_not_empty; boost::mutex monitor; }; bounded_buffer buf(2); void sender() { int n = 0; while (n < 100) { buf.send(n); std::cout << "sent: " << n << std::endl; ++n; } buf.send(-1); } void receiver() { int n; do { n = buf.receive(); std::cout << "received: " << n << std::endl; } while (n != -1); // -1 indicates end of buffer } int main(int, char*[]) { boost::thread thrd1(&sender); boost::thread thrd2(&receiver); thrd1.join(); thrd2.join(); return 0; }
Typical output (dependent on scheduling policies) is:
sent: 0 sent: 1 received: 0 received: 1 sent: 2 sent: 3 received: 2 received: 3 sent: 4 received: 4
Revised 05 November, 2001
© Copyright William E. Kempf 2001 all rights reserved.