Using the Condition Variable for Producer-Consumer Synchronization
Condition variables are ideal for implementing producer-consumer synchronization, a common form of condition synchronization used to mediate the transfer of information between threads. Under this form of synchronization, a consumer thread attempts to get information from a producer thread or threads. If that information is not immediately available, it waits for a producer thread to signal when the information becomes available.
Similarly, a producer thread attempts to send information to a consumer thread or threads. If no consumer thread is waiting for the information or if the information cannot be buffered, then the producer thread waits for a consumer thread to signal that it has received or is ready to receive information.
Example 31 uses the linked-list template
RWTValSlist, from the Essential Tools Module, and the Synchronization package classes
RWMutexLock and
RWCondition to illustrate how producer-consumer synchronization can be used to turn a simple queue into an elegant and simple mechanism for efficiently communicating between threads.
Example 31 – Using condition variables for producer-consumer synchronization
#include <rw/tvslist.h>
#include <rw/sync/RWMutexLock.h>
#include <rw/sync/RWCondition.h>
template <class T> class PCQueue {
private:
RWMutexLock mutex; // 1
RWCondition notEmpty; // 2
RWCondition notFull;
RWTValSlist<T> queue; // 3
size_t maxSize;
public:
PCQueue(size_t size=0) // 4
: maxSize(size), notEmpty(mutex), notFull(mutex) // 5
{
}
T read(void)
{
RWMutexLock::LockGuard guard(mutex); // 6
while(queue.isEmpty()) { // 7
notEmpty.wait(); // 8
}
T result = queue.removeFirst(); // 9
notFull.signal(); // 10
return result;
}
void write(T t)
{
RWMutexLock::LockGuard guard(mutex);
while(maxSize != 0 && queue.entries() <= maxSize) { // 11
notFull.wait(); // 12
}
queue.append(t); // 13
notEmpty.signal(); // 14
}
};
A full implementation of a producer-consumer queue would also have member functions for testing whether the queue was currently empty or full, thereby allowing threads to avoid blocking during a read or write operation. These functions and more have already been defined in an existing family of producer-consumer queue and stack classes in the Interthread Communication package. These classes are described in
The Producer‑Consumer Classes.