In waitForWaxing( )
, the waxOn flag is checked, and if it is false, the calling thread is suspended by calling wait( ) on the Condition object. It’s important that this occur inside a guarded clause, in which the thread has acquired the lock (here, by creating a Guard object). When you call wait( ), the thread is suspended andIn order for a thread to wake up from a wait( )
, it must first reacquire the mutex that it released when it entered the wait( ). The thread will not wake up until that mutex becomes available.The call to wait( )
is placed inside a while loop that checks the condition of interest. This is important for two reasons:· It is possible that when the thread gets a signal( )
, some other condition has changed that is not associated with the reason that we called wait( ) here. If that is the case, this thread should be suspended again until its condition of interest changes.· By the time this thread awakens from its wait( )
, it’s possible that some other task has changed things such that this thread is unable or uninterested in performing its operation at this time. Again, it should be re-suspended by calling wait( ) again.Because these two reasons are always present when you are calling wait( )
, always write your call to wait( ) inside a while loop that tests for your condition(s) of interest.WaxOn::run( )
represents the first step in the process of waxing the car, so it performs its operation (a call to sleep( ) to simulate the time necessary for waxing). It then tells the car that waxing is complete, and calls waitForBuffing( ), which suspends this thread with a wait( ) until the WaxOff process calls buffed( ) for the car, changing the state and calling notify( ). WaxOff::run( ), on the other hand, immediately moves into waitForWaxing( ) and is thus suspended until the wax has been applied by WaxOn and waxed( ) is called. When you run this program, you can watch this two-step process repeat itself as control is handed back and forth between the two threads. When you press theProducer-consumer relationships
A common situation in threading problems is the
To show this problem, consider a machine that has three tasks: one to make toast, one to butter the toast, and one to put jam on the buttered toast.
//: C11:ToastOMatic.cpp
// Problems with thread cooperation.
//{L} ZThread
#include "zthread/Thread.h"
#include "zthread/Mutex.h"
#include "zthread/Guard.h"
#include "zthread/Condition.h"
#include "zthread/ThreadedExecutor.h"
#include
#include
#include
using namespace ZThread;
using namespace std;
// Apply jam to buttered toast:
class Jammer : public Runnable {
Mutex lock;
Condition butteredToastReady;
bool gotButteredToast;
int jammed;
public:
Jammer(): butteredToastReady(lock) {
gotButteredToast = false;
jammed = 0;
}
void moreButteredToastReady() {
Guard
gotButteredToast = true;
butteredToastReady.signal();
}
void run() {
try {
while(!Thread::interrupted()) {
{
Guard
while(!gotButteredToast)
butteredToastReady.wait();
jammed++;
}
cout << "Putting jam on toast " << jammed << endl;
{
Guard
gotButteredToast = false;
}
}
} catch(Interrupted_Exception&) { /* Exit */ }
cout << "Jammer off" << endl;
}
};
// Apply butter to toast:
class Butterer : public Runnable {
Mutex lock;
Condition toastReady;
CountedPtr
bool gotToast;
int buttered;
public:
Butterer(CountedPtr
: jammer(j), toastReady(lock) {
gotToast = false;
buttered = 0;
}
void moreToastReady() {
Guard
gotToast = true;
toastReady.signal();
}