Another definition of monitor is a thread-safe class, object, or module that wraps around a mutex in order to safely allow access to a method or variable by more than one thread.
Note that without this mutual exclusion, two threads could cause money to be lost or gained for no reason.
A busy waiting loop will not work, as mutual exclusion will prevent any other thread from entering the monitor to make the condition true.
It is hard to decide an appropriate amount of waiting time: too small and the thread will hog the CPU, too big and it will be apparently unresponsive.
Thus, any code that accesses the queue constitutes a critical section that must be synchronized by mutual exclusion.
If producer/consumer threads are allowed to be interleaved during the calls to enqueue/dequeue, then inconsistent state of the queue can be exposed leading to race conditions.
This method assures that an inconsistent state does not occur, but wastes CPU resources due to the unnecessary busy-waiting.
Likewise, even if consumers are blocked for a long time on processing their current tasks and the queue is full, producers are always busy-waiting.
: Mutexes themselves can also be spin-locks which involve busy-waiting in order to get the lock, but in order to solve this problem of wasted CPU resources, we assume that queueLock is not a spin-lock and properly uses a blocking lock queue itself.)
In most types of monitors, these other threads may signal the condition variable c to indicate that assertion Pc is true in the current state.
In the producer-consumer example described above, the queue must be protected by a unique mutex object, m. The "producer" threads will want to wait on a monitor using lock m and a condition variable
The classic solution is to use two monitors, comprising two condition variables sharing one lock on the queue: This ensures concurrency between the producer and consumer threads sharing the task queue, and blocks the threads that have nothing to do rather than busy-waiting as shown in the aforementioned approach using spin-locks.
A variant of this solution could use a single condition variable for both producers and consumers, perhaps named "queueFullOrEmptyCV" or "queueSizeChangedCV".
Here is an example pseudocode implementation of parts of a threading system and mutexes and Mesa-style condition variables, using test-and-set and a first-come, first-served policy: The original proposals by C. A. R. Hoare and Per Brinch Hansen were for blocking condition variables.
(When the condition variable can be queried as to the number of threads waiting on its queue, more sophisticated contracts can be given.
For example, a useful pair of contracts, allowing occupancy to be passed without establishing the invariant, is: (See Howard[4] and Buhr et al.[5] for more.)
The only difference is that the producer/consumer example assumed a regular non-thread-safe queue and was using a standalone mutex and condition variables, without these details of the monitor abstracted away as is the case here.
With nonblocking condition variables, the signal operation is often called notify — a terminology we will follow here.
It is also common to provide a notify all operation that moves all threads waiting on a condition variable to the e queue.
As a variation on this scheme, the notified thread may be moved to a queue called w, which has priority over e. See Howard[4] and Buhr et al.[5] for further discussion.
As an example of "hinting," consider a bank account in which a withdrawing thread will wait until the account has sufficient funds before proceeding In this example, the condition being waited for is a function of the amount to be withdrawn, so it is impossible for a depositing thread to know that it made such a condition true.
It makes sense in this case to allow each waiting thread into the monitor (one at a time) to check if its assertion is true.
The contract for wait is Brinch Hansen and Hoare developed the monitor concept in the early 1970s, based on earlier ideas of their own and of Edsger Dijkstra.
[8] Brinch Hansen published the first monitor notation, adopting the class concept of Simula 67,[1] and invented a queueing mechanism.
Monitors (and Concurrent Pascal) were soon used to structure process synchronization in the Solo operating system.
When library calls are used, it is up to the programmer to explicitly mark the start and end of code executed with mutual exclusion.
a
and
b
. After Buhr
et al.
a
and
b