Process Synchronization
Process Synchronization
This can lead to the inconsistency of shared data. So the change made by one
process not necessarily reflected when other processes accessed the same shared
data. To avoid this type of inconsistency of data, the processes need to be
synchronized with each other.
• The entry to the critical section is handled by the wait() function, and it is
represented as P().
• The exit from a critical section is controlled by the signal() function,
represented as V().
In the critical section, only a single process can be executed. Other processes,
waiting to execute their critical section, need to wait until the current process
completes its execution.
Initially flag[0] = flag[1] = false, and value of turn is immaterial (but either 0 or 1).
To enter the critical section, process Pi first sets flag[i] to be true and then sets
turn to the value of j, thereby asserting that if either process wishes to enter
critical section, it can do so. If both try to enter simultaneously, turn will be set to
both I and j at roughly same time. Only one of these assignments will last, the
other will occur, but will be overwritten immediately. The eventual value of turn
decides which of the two processes is allowed to enter its critical section first.
When a process executes the wait operation and finds that semaphore value is not
positive, it must wait. However, rather than waiting, the process can block itself. The
block operation places a process into a waiting queue associated with semaphore,
and the state of the process is switched to the waiting state.
void wait(semaphore S){
S.value--;
if(S.value < 0){
Add this process to S.L;
Block();
}
}
void signal(semaphore S){
S.value++;
if(S.value <= 0){
remove a process from S.L;
wakeup();
}
}
They can only be either 0 or 1. They are also known as mutex locks, as the locks can
provide mutual exclusion. All the processes can share the same mutex semaphore
that is initialized to 1. Then, a process has to wait until the lock becomes 0. Then, the
process can make the mutex semaphore 1 and start its critical section. When it
completes its critical section, it can reset the value of mutex semaphore to 0 and
some other process can enter its critical section.
The canonical use of a semaphore is a lock associated with some resource so that
only one thread at a time has access to the resource. In the example below, we
have one piece of global data, the number of tickets remaining to sell, that we want
to coordinate the access by multiple threads. In this case, a binary semaphore
serves as a lock to guarantee that at most one thread is examining or changing the
value of the variable at any given time.
/** * Our main is creates the initial semaphore lock in an unlocked state
and sets up all of the ticket seller threads, and lets them run to
completion. They should all finish when all tickets have been sold. By
running with the -v flag, it will include the trace output from the thread
library. */
while (!done) {
} else {
numTickets--;
numSoldByThisThread++;
int i;
InitThreadPackage(verbose);
RunAllThreads();
SemaphoreSignal(hydrogenReady);
SemaphoreWait(hydrogenReady);
SemaphoreSignal(oxygenReady);
SemaphoreSignal(oxygenReady);
Library Analogy:
Suppose a library has 10 identical study rooms, to be used by one student at a time. Students
must request a room from the front desk if they wish to use a study room. If no rooms are free,
students wait at the desk until someone relinquishes a room. When a student has finished using
a room, the student must return to the desk and indicate that one room has become free.
In the simplest implementation, the clerk at the front desk knows only the number of free rooms
available, which they only know correctly if all of the students actually use their room while
they've signed up for them and return them when they're done. When a student requests a room,
the clerk decreases this number. When a student releases a room, the clerk increases this
number. The room can be used for as long as desired, and so it is not possible to book rooms
ahead of time.
In this scenario the front desk count-holder represents a counting semaphore, the rooms are the
resource, and the students represent processes/threads.
The value of the semaphore in this scenario is initially 10, with all rooms empty.
When a student requests a room, they are granted access, and the value of the semaphore is
changed to 9.
After the next student comes, it drops to 8, then 7 and so on.
If someone requests a room and the current value of the semaphore is 0,[2] they are forced to
wait until a room is freed (when the count is increased from 0). If one of the rooms was released,
but there are several students waiting, then any method can be used to select the one who will
occupy the room.
Student needs to inform the clerk about releasing their room only after really leaving it.
Producer-Consumer Problem
A producer should not produce items into the buffer when the consumer is consuming
an item from the buffer and vice versa. So the buffer should only be accessed by the
producer or consumer at a time.
The producer consumer problem can be resolved using semaphores. The codes for
the producer and consumer process are given as follows:
Producer Process
The code that defines the producer process is given below:
do {
. PRODUCE ITEM
wait(empty);
wait(mutex);
signal(mutex);
signal(full);
} while(1);
Consumer Process
The code that defines the consumer process is given below:
do {
wait(full);
wait(mutex); .
signal(mutex);
signal(empty);
. CONSUME
ITEM .
} while(1);
The Dining Philosopher Problem –
The Dining Philosopher Problem states that K philosophers seated around a circular
table with one chopstick between each pair of philosophers. There is one chopstick
between each philosopher. A philosopher may eat if he can pick up the two chopsticks
adjacent to him. One chopstick may be picked up by any one of its adjacent followers
but not both.
Semaphore Solution to Dining Philosopher –
do{
wait(chopstick[i]);
wait(chopstick[(i+1)%5]);
…
Eat
…
signal(chopstick[i]);
signal(chopstick[i+1]%5);
…
Think
…
}while(1);
A philosopher tries to grab the chopstick by executing a wait operation on that
semaphore, she releases her chopsticks by executing the signal operation on
appropriate semaphores. Thus the shared data are semaphore chopstick[5]; where
all the elements are initialized to 1.
} while(true);
Reader process:
1. Reader requests the entry to critical section.
2. If allowed:
• it increments the count of number of readers inside the critical section. If
this reader is the first reader entering, it locks the wrt semaphore to restrict
the entry of writers if any reader is inside.
• It then, signals mutex as any other reader is allowed to enter while others
are already reading.
• After performing reading, it exits the critical section. When exiting, it
checks if no more reader is inside, it signals the semaphore “wrt” as now,
writer can enter the critical section.
3. If not allowed, it keeps on waiting.
do {
// Reader wants to enter the critical section
wait(mutex);