Task 1: Understanding Race Conditions and Critical Sections
1. Race Condition:
- Define: A race condition occurs when multiple processes or threads access shared resources
concurrently, and the final outcome depends on the order of execution, leading to inconsistent or
incorrect results.
- Example: Two threads incrementing a shared counter without synchronization. If both read
the counter simultaneously, they may overwrite each other’s results, leading to incorrect
increments.
2. Critical Section:
- Identify: In a given code snippet, look for sections where shared resources (variables, files,
etc.) are accessed or modified. These are critical sections.
- Example:
int counter = 0;
void increment() {
counter++; // Critical section
3. Mutual Exclusion:
- Explain: Mutual exclusion ensures that only one process or thread can execute the critical
section at a time, preventing race conditions.
- Importance: It guarantees correctness and consistency in concurrent programs.
Task 2: Semaphores
1. Semaphore Definition:
- A semaphore is an integer variable used for signaling between processes/threads. It supports
two atomic operations:
- `wait()` (or `P()`): Decrements the semaphore. If the value is negative, the process/thread
blocks.
- `signal()` (or `V()`): Increments the semaphore. If the value is non-negative, a blocked
process/thread is woken up.
2. Binary Semaphore Implementation:
- Example in C:
#include <semaphore.h>
sem_t sem;
sem_init(&sem, 0, 1); // Initialize semaphore to 1 (binary semaphore)
sem_wait(&sem); // wait()
sem_post(&sem); // signal()
3. Producer-Consumer Problem:
- Use semaphores to synchronize access to a shared buffer.
- Example:
em_t empty, full, mutex;
void producer() {
while (true) {
produce_item();
sem_wait(&empty);
sem_wait(&mutex);
add_to_buffer();
sem_post(&mutex);
sem_post(&full);
4. Issues with Semaphores:
- Deadlock: Circular waiting for resources.
- Starvation: Some processes/threads may never get access to the resource.
Task 3: Mutexes
1. Mutex Definition:
- A mutex is a locking mechanism that ensures mutual exclusion. It supports:
- `lock()`: Acquires the lock.
- `unlock()`: Releases the lock.
2. Mutex Implementation:
- Example in C:
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
pthread_mutex_lock(&mutex); // lock()
pthread_mutex_unlock(&mutex); // unlock()
3. Protecting Critical Sections:
- Use mutexes to ensure only one thread accesses shared resources at a time.
4. Comparison with Semaphores:
- Mutexes are binary (locked/unlocked), while semaphores can have multiple values.
- Mutexes are typically used for mutual exclusion, while semaphores can be used for signaling.
Task 4: Monitors
1. Monitor Definition:
- A monitor is a high-level synchronization construct that encapsulates shared data and
operations. It includes:
- Condition variables: For signaling and waiting.
- Mutex: To ensure mutual exclusion.
2. Monitor Implementation:
- Example in Java:
java
class Monitor {
private int sharedData;
private final Object lock = new Object();
private boolean condition = false;
public void method() {
synchronized (lock) {
while (!condition) {
lock.wait();
// Critical section
lock.notifyAll();
}} }
3. Dining Philosophers Problem:
- Use a monitor to ensure philosophers don’t deadlock while picking up forks.
4. Advantages and Disadvantages:
- Advantages: High-level abstraction, easier to use than semaphores.
- Disadvantages: Limited flexibility compared to low-level primitives.
Task 5: Performance Evaluation
1. Program to Measure Performance:
- Write a program that creates multiple threads and uses semaphores, mutexes, and monitors to
synchronize access to shared resources.
- Measure execution time, throughput, and resource utilization.
2. Analysis:
- Compare the performance of semaphores, mutexes, and monitors under different loads (e.g.,
low, medium, high).
- Identify factors like context switching overhead, contention, and scalability.
3. Trade-offs:
- Correctness vs. Performance: Strong synchronization ensures correctness but may reduce
performance due to overhead.
Additional Considerations
1. Context Switching:
- Frequent context switching can degrade performance. Minimize it by optimizing
synchronization mechanisms.
2. Synchronization Primitives:
- Explore spinlocks (busy-waiting) and barriers (synchronization points for multiple threads).
3. Hardware Support:
- Investigate atomic operations (e.g., compare-and-swap) and hardware-level locks.
4. Distributed Systems:
- Discuss challenges like network latency, partial failures, and consensus algorithms (e.g.,
Paxos, Raft).