Generalize the signal pattern so that it works both ways.
Thread A
has to wait for Thread B
and vice versa. In other words, given this code:
Thread A | Thread B |
---|---|
statement a1 | statement b1 |
statement a2 | statement b2 |
we want to guarantee that a1
happens before b2
and b1
happens before a2
.
In writing your solution, be sure to specify the names and initial values of your semaphores (little hint there).
Your solution should not enforce too many constraints. For example, we don’t care about the order of a1
and b1
.
In your solution, either order should be possible.
This synchronization problem has a name; it’s a rendezvous. The idea is that two threads rendezvous at a point of execution, and neither is allowed to proceed until both have arrived.
The chances are good that you were able to figure out a solution, but if not, here is a hint.
Create two semaphores, named aArrived
and bArrived
, and initialize them both to zero.
As the names suggest, aArrived
indicates whether Thread A
has arrived at the rendezvous, and bArrived
likewise.
Add semaphores to the following example to enforce mutual exclusion to the shared variable count:
Thread A | Thread B |
---|---|
count = count + 1 | count = count + 1 |
Create a semaphore named mutex
that is initialized to 1.
A value of one means that a thread may proceed and access the shared variable;
a value of zero means that it has to wait for another thread to release the mutex.
Generalize the previous solution so that it allows multiple threads to run in the critical section at the same time,
but it enforces an upper limit on the number of concurrent threads. In other words, no more than n
threads can run
in the critical section at the same time.
This pattern is called a multiplex
. In real life, the multiplex problem occurs at busy nightclubs where there is
a maximum number of people allowed in the building at a time, either to maintain fire safety or to create the illusion of exclusivity.
At such places a bouncer usually enforces the synchronization constraint by keeping track of the number of people inside and barring arrivals when the room is at capacity. Then, whenever one person leaves another is allowed to enter. Enforcing this constraint with semaphores may sound difficult, but it is almost trivial.
Consider again the Rendezvous problem from Section 3.3. A limitation of the solution we presented is that it does not work with more than two threads.
Generalize the rendezvous solution. Every thread should run the following code:
rendezvous
critical point
The synchronization requirement is that no thread executes critical point
until after all threads have executed rendezvous
.
You can assume that there are n
threads and that this value is stored in a variable, n
, that is accessible from all threads.
When the first n − 1
threads arrive they should block until the nth
thread arrives, at which point all the threads may proceed.
n = the number of threads
count = 0
mutex = Semaphore(1)
barrier = Semaphore(0)
count
keeps track of how many threads have arrivedmutex
provides exclusive access to count so that threads can increment it safelybarrier
is locked (zero or negative) until all threads arrive; then it should be unlocked (1 or more).