Semaphores work just fine for synchronization, but they are totally unstructured (in the same sense that GOTOs and pointers are unstructured). There has been some work in creating language features to provide mutual exclusion. Prominent in these efforts has been the monitor.
A monitor simply defines some code to be a critical section, and
depends on the compiler to insert synchronization primitives as
needed. It would be very simple to create a monitor that looked like
a C++ class; something like
monitor {
public:
proc1;
proc2;
};
The semantics of this is that appropriate entry and exit code (like a boolean semaphore down and up) is inserted for any time a call is made from outside the monitor to inside the monitor.
This provides mutual exclusion, but not resource control (remember bounded buffer required both). Monitors provide a mechanism for resource control though ``condition variables'' and signal and wait procedures.
A condition is something you need to be true before you can continue. If you test for the condition and it doesn't hold, you need to call wait to wait for it to become true. If you cause a condition to become true, then you call signal to notify anybody waiting on it that it has become true. When you call wait, you release the monitor lock; otherwise, nobody else could get in to let you go again.
As an aside, there have been many subtle variations on the precise
semantics of signal and wait. Here's a
brief writeup of Hoare and Mesa monitors.
Here's the producer-consumer problem from last time, recoded using a monitor.
| Producer | Consumer | Monitor |
|---|---|---|
while (1) {
item = produce();
put(item);
}
|
while (1) {
item = get();
consume(item);
}
|
monitor {
int space = N;
int full = 0;
condition cspace;
condition cfull;
Item get {
if (full == 0) wait(cfull);
// get item from queue
full--;
space++;
if (space == 1) signal(cspace);
return(item);
}
void put(Item item) {
if (space == 0) wait(cspace);
// put item on queue
full++;
space--;
if (full == 1) signal(cfull);
}
|
This moves the real work into the put and get routines; the program itself doesn't have to worry about it.