Mutex cater to the most general form of resource sharing, but
sometimes threads have more specific sharing requirements.Condition Variables allow
us to express and control a directed dependency between threads. In general
this means that one group of threads should only execute when a given condition
becomes true, and this condition will be satisfied by another group of threads.
When thread operation is coordinated by a condition variable, one
group of threads waits until a condition is triggered, at which point one or
more of the waiting threads is woken.
An example: there are two groups of threads: one producing
something, and the other consuming it. The Producer-Consumer pattern is also
useful outside multi-threaded programming, where it allows you to decouple
data/event/request/whatever production from consumption. For our contrived example,
we produce and consume characters from the alphabet.
std::queue<char> producedChars;
boost::condition characterAvailable;
boost::mutex characterMutex;
void conditionDataProducer()
{
unsigned char c = 'A';
for(uint i = 0; i < numCharacters;)
{
if((c >= 'A' && c <= 'Z'))
{
boost::mutex::scoped_lock lock(characterMutex);
producedChars.push(c++);
characterAvailable.notify_one();
++i;
}
else
{
c = 'A';
}
}
boost::mutex::scoped_lock lock(characterMutex);
producedChars.push(EOF);
characterAvailable.notify_one();
}
void conditionDataConsumer()
{
boost::mutex::scoped_lock lock(characterMutex);
while(true)
{
// on entry, releases mutex and suspends this thread
// on return, reacquires mutex
while(producedChars.empty())
{
characterAvailable.wait(lock);
}
if(producedChars.front() == EOF) break;
producedChars.pop();
}
}
Take a look at the consumer first: it acquires a mutex and then
uses the condition variable to
wait
. When wait
is
called the mutex is unlocked and the calling thread is suspended. The consumer
thread will now only be resumed when the condition represented by character Available
becomes
true.
The producer simply pushes characters onto the shared container
and then calls
notify_one
. This
will allow one of the threads waiting on this condition (a consumer) to resume
and process the new data. This will be much more efficient than having
consumers endlessly polling an empty queue.
Condition
variables are also useful in implementing the Monitor Object concurrency
pattern, which we talk about next.
See also:
No comments:
Post a Comment