Classical Problems of Synchronization
Classical Problems of Synchronization
Each of these problems represents a real-world concurrency issue and is solved using
synchronization mechanisms such as semaphores, mutexes, and condition variables.
Problem Description
A shared buffer of fixed size (N) is used for communication between producers and
consumers.
Producers add data to the buffer but must wait if the buffer is full.
Consumers remove data from the buffer but must wait if the buffer is empty.
Synchronization Issues
Without proper synchronization, the following problems can occur:
1. Producers write to a full buffer, causing data loss.
2. Multiple producers write to the same slot, leading to corruption.
3. Consumers read from an empty buffer, leading to incorrect behavior.
4. Multiple consumers read from the same slot, leading to data inconsistency.
5. A consumer reads a slot before a producer has finished writing to it, leading to
partial data consumption.
Solution Using Semaphores
To ensure synchronization, we use:
Mutex (Binary Semaphore): Ensures exclusive access to the buffer.
Full (Counting Semaphore): Tracks the number of occupied slots.
Empty (Counting Semaphore): Tracks the number of empty slots.
Initialization
mutex = 1 → Allows exclusive access to the buffer.
full = 0 → No items in the buffer initially.
empty = N → All slots are available at the beginning.
Producer Process
Producer()
{
wait(empty); // Check if space is available
wait(mutex); // Lock buffer
// Add item to buffer
signal(mutex); // Unlock buffer
signal(full); // Increase count of occupied slots
}
Consumer Process
Consumer()
{
wait(full); // Check if any item is available
wait(mutex); // Lock buffer
// Remove item from buffer
signal(mutex); // Unlock buffer
signal(empty); // Increase count of empty slots
}
Problem Description
Multiple readers can read at the same time without conflicts.
A writer must have exclusive access to the resource (no other readers or writers
should access it).
Synchronization Issues
1. Multiple writers modifying data simultaneously can lead to corruption.
2. Readers reading while a writer is modifying data can cause inconsistencies.
3. Starvation occurs when either readers or writers are indefinitely blocked.
Solution Using Semaphores
To ensure synchronization, we use:
Mutex (Binary Semaphore): Provides mutual exclusion for modifying the readcount.
wrt (Binary Semaphore): Ensures only one writer at a time.
readcount (Integer): Keeps track of the number of active readers.
Initialization
mutex = 1 → Controls access to readcount.
wrt = 1 → Controls access to the resource.
readcount = 0 → No readers initially.
Writer Process
Writer()
{
wait(wrt); // Ensure exclusive access
// Perform writing
signal(wrt); // Release access
}
Reader Process
Reader()
{
wait(mutex); // Lock `readcount`
readcount++;
if (readcount == 1) // First reader blocks writers
wait(wrt);
signal(mutex); // Unlock `readcount`
// Perform reading
Problem Description
N philosophers sit at a circular table with N chopsticks (one between each pair).
Each philosopher thinks and eats in cycles.
To eat, a philosopher needs two chopsticks.
They must pick one at a time, leading to a potential deadlock.
Synchronization Issues
1. Deadlock occurs if every philosopher picks up their left chopstick and waits for the
right one.
2. Starvation occurs if some philosophers never get both chopsticks.
3. Concurrency Issues arise if two philosophers pick up the same chopstick.
Linux
Before kernel 2.6: Used interrupt disabling.
After kernel 2.6: Fully preemptive, using:
o Semaphores
o Atomic integers
o Spinlocks
o Reader-writer locks
POSIX Synchronization
Mutex locks: Provides mutual exclusion.
Semaphores:
o Named: Used between unrelated processes.
o Unnamed: Used within the same process.
Condition variables: Used for signaling between threads.
Java Synchronization
Java provides:
Monitors: Implicit locks using synchronized keyword.
Reentrant Locks: Explicit locks with flexibility.
Semaphores: Controls access to resources.
Condition Variables: Enables inter-thread communication.
Alternative Approaches
Transactional Memory: Allows atomic memory transactions.
OpenMP: Parallel programming API.
Functional Programming: Eliminates side effects for better concurrency