|
|
Boost.Threadscondition |
A condition is a synchronization primitive that can be employed to suspend a thread and reactivate it when a particular condition is met. A condition always works in conjunction with a mutex model. The mutex model must be locked prior to waiting on the condition, which is insured by passing a lock model to the wait methods. While the thread is suspended waiting on the condition the mutex model is unlocked. When the thread returns from a call to one of the wait methods the mutex model is again locked.
The condition type is often used to implement the Monitor Object pattern.
#include <boost/thread/condition.hpp>
class condition : private boost::noncopyable
{
public:
condition();
~condition();
void notify_one();
void notify_all();
template <typename L>
void wait(L& lock);
template <typename L, typename Pr>
void wait(L& lock, Pr pred);
template <typename L>
bool timed_wait(L& lock, const xtime& xt);
template <typename L, typename Pr>
bool timed_wait(L& lock, const xtime& xt, Pr pred);
};
condition();
Constructs a condition.
~condition();
Destructs a condition.
void notify_one();
If there are any waiting threads releases at least one.
void notify_all();
Releases all waiting threads.
template <typename L>
void wait(L& lock);
Releases the lock on the mutex model associated with lock, waits for the condition to be notified and then reacquires the lock all in an atomic fashion. This version should always be used within a loop checking that the state logically associated with the condition has become true. With out the loop race conditions can ensue due to possible "spurious wake ups". The second version encapsulates this idiom internally and should generally be the preferred method.
template <typename L, typename Pr>
void wait(L& lock, Pr pred);
Releases the lock on the mutex model associated with lock, waits for the condition to be notified and for pred to be true then reacquires the lock all in an atomic fashion.
template <typename L>
bool timed_wait(L& lock, const xtime& xt);
Releases the lock on the mutex model associated with the lock, waits for the condition to be notified or until xt and then reacquires the lock all in an atomic fashion. If the wait timed out the return value is false. This version should always be used within a loop checking that the state logically associated with the condition has become true. With out the loop race conditions can ensue due to "spurious wake ups". The second version encapsulates this idiom internally and should generally be the preferred method.
template <typename L, typename Pr>
bool timed_wait(L& lock, const xtime& xt, Pr pred);
Releases the lock on the mutex model associated with the lock, waits for the condition to be notified and for pred to be true or until xt and then reacquires the lock all in an atomic fashion. If the wait timed out the return value is false.
#include <iostream>
#include <vector>
#include <boost/utility.hpp>
#include <boost/thread/condition.hpp>
#include <boost/thread/thread.hpp>
class bounded_buffer : private boost::noncopyable
{
private:
int begin, end, buffered;
std::vector<int> circular_buf;
boost::condition buffer_not_full, buffer_not_empty;
boost::mutex monitor;
typedef boost::mutex::lock lock;
public:
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 && !finished)
buffer_not_empty.wait(lk);
int i = circular_buf[begin];
begin = (begin+1) % circular_buf.size();
--buffered;
buffer_not_full.notify_one();
return i;
}
};
bounded_buffer buf(2);
void sender(void*) {
int n = 0;
while (n < 100) {
buf.send(n);
std::cout << "sent: " << n << std::endl;
++n;
}
buf.send(-1);
}
void receiver(void*) {
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::create(&sender, 0);
boost::thread::create(&receiver, 0);
boost::thread::join_all();
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
Douglas Schmidt, Michael Stal, Hans Rohnert, Frank Buschmann: Pattern-Oriented Software Architecture - Volume 2 (Patterns for Concurrent and Networked Objects), Wiley, 2000.
Copyright William E. Kempf 2001 all rights reserved.