|
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 ScopedTimedLock> bool timed_wait(ScopedTimedLock& lock, const xtime& xt);
Requires: ScopedTimeLock meets the ScopedTimedLock 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 ScopedTimedLock, typename Pr> bool timed_wait(ScopedTimedLock& lock, const xtime& xt, Pr pred);
Requires: ScopedTimeLock meets the
ScopedTimedLock 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 01 October, 2001
© Copyright William E. Kempf 2001 all rights reserved.