In a multiprogramming environment, multiple processes can execute concurrently and share the same memory or data. When processes are allowed to execute concurrently, they may be interrupted at any point, leaving shared data in an inconsistent state.
Process Synchronization is the coordination of execution of multiple processes in a multi-processor system to ensure that they access shared resources in a mutually exclusive manner.
A Race Condition occurs when several processes access and manipulate the same data concurrently, and the outcome of the execution depends on the particular order in which the access takes place.
Imagine two threads, Thread A and Thread B, both trying to increment a shared variable counter = 5.
The code counter++ is actually executed by the CPU in three distinct low-level machine instructions:
counter from memory into a CPU register.If Thread A executes Step 1 and Step 2, its register contains 6. But before it can execute Step 3, a context switch happens!
Now Thread B executes Steps 1, 2, and 3. It reads 5, increments to 6, and writes 6 to memory.
Finally, Thread A resumes and executes Step 3, writing its register (6) to memory.
Even though two threads incremented the counter, the final value is 6 instead of 7. The data has been corrupted because the execution was interleaved.
To solve the race condition, we need to ensure that the three steps of counter++ are executed atomically (as one uninterruptible unit).
Modern processors provide special hardware instructions to help with synchronization:
Because these are hardware-level instructions, they are guaranteed to be atomic. No context switch can interrupt a Test-and-Set instruction halfway through.
A Semaphore is an integer variable that, apart from initialization, is accessed only through two standard atomic operations: wait() and signal() (originally termed P() and V() by Edsger Dijkstra).
wait(S): If the semaphore $S \le 0$, the process blocks and waits. If $S > 0$, it decrements $S$ and continues execution.signal(S): Increments the semaphore $S$. If any processes are blocked waiting for $S$, one is woken up.0 or 1. Used to provide mutual exclusion. A process calls wait() before entering its critical section, and signal() after leaving.Warning: Incorrect use of semaphores can result in timing errors that are notoriously difficult to detect, since these errors happen only if particular execution sequences take place, and these sequences are rare.