Computer Science
Grade 11
20 min
3. Synchronization Primitives: Mutexes, Semaphores, and Condition Variables
Explore different synchronization primitives such as mutexes, semaphores, and condition variables, and their use cases.
Tutorial Preview
1
Introduction & Learning Objectives
Learning Objectives
Define and explain the purpose of race conditions and critical sections in concurrent programming.
Differentiate between a mutex, a semaphore, and a condition variable, identifying the ideal use case for each.
Analyze a concurrent programming problem and select the appropriate synchronization primitive to prevent data corruption.
Implement the standard lock/unlock pattern for a mutex to protect a critical section in pseudocode.
Trace the execution of threads in a producer-consumer scenario using semaphores and a mutex.
Explain how a condition variable prevents inefficient busy-waiting.
Identify the causes of common synchronization issues like deadlock and starvation.
Imagine two people trying to edit the same sentence in a shared document at the exact s...
2
Key Concepts & Vocabulary
TermDefinitionExample
Race ConditionA situation where the outcome of a program depends on the unpredictable sequence or timing of operations from multiple threads accessing a shared resource.Two threads try to increment a shared counter (initially 0) at the same time. Both read the value 0, both calculate 0+1=1, and both write 1 back. The final value is 1, when it should be 2.
Critical SectionA segment of code that accesses a shared resource (like a variable or data structure) and must not be executed by more than one thread at a time to avoid race conditions.In the shared counter example, the lines of code that read the counter's value, increment it, and write it back form the critical section.
Mutex (Mutual Exclusion)A locking mechanism that ensures only one thread can enter a crit...
3
Core Syntax & Patterns
Mutex Lock/Unlock Pattern
mutex.lock()
// Critical Section: Access shared data here
mutex.unlock()
This is the fundamental pattern for protecting a critical section. Always acquire the lock before touching shared data and, crucially, always release the lock afterward, even if an error occurs.
Semaphore Wait/Signal Pattern
semaphore.wait() // Decrements counter, may block if 0
// Access the limited resource
semaphore.signal() // Increments counter, may unblock a waiting thread
Used to control access to a pool of N resources. 'wait' (also called P or down) requests a resource, and 'signal' (also called V or up) releases it for others to use.
Condition Variable Wait/Signal Pattern
mutex.lock()
while (condition_is_false) {
condition_variable.wait(mut...
4 more steps in this tutorial
Sign up free to access the complete tutorial with worked examples and practice.
Sign Up Free to ContinueSample Practice Questions
Challenging
In a complex system, a high-priority thread is repeatedly prevented from acquiring a mutex because a continuous stream of lower-priority threads acquires and releases it first. The high-priority thread is not deadlocked but is not making progress. This problem is known as:
A.Lock convoy
B.Starvation
C.Spurious wakeup
D.Deadly embrace
Challenging
Consider the standard pseudocode for a producer: `1: empty_slots.wait(); 2: buffer_mutex.lock(); 3: // add item; 4: buffer_mutex.unlock(); 5: full_slots.signal();`. What would be the most severe consequence if lines 1 and 2 were swapped?
A.The program would work correctly but might be less efficient.
B.race condition would occur when multiple producers modify the buffer.
C.deadlock could occur if the buffer becomes full.
D.The consumer would be able to read from an empty buffer.
Challenging
A developer replaces one large mutex that locks an entire data structure (coarse-grained locking) with multiple smaller mutexes, each protecting a different part of the structure (fine-grained locking). What is a potential new risk introduced by this change?
A.The program will always be slower due to the overhead of acquiring multiple locks.
B.It eliminates the possibility of race conditions entirely, so there is no new risk.
C.It makes the use of condition variables on the data structure impossible.
D.It increases the complexity and the risk of a lock-ordering deadlock if an operation needs multiple locks.
Want to practice and check your answers?
Sign up to access all questions with instant feedback, explanations, and progress tracking.
Start Practicing FreeMore from I. Concurrent and Parallel Programming: Unleashing the Power of Multiple Cores
1. Introduction to Concurrency: Threads and Processes
2. Thread Management: Creation, Termination, and Synchronization
4. Deadlocks and Race Conditions: Identifying and Avoiding Problems
5. Parallel Programming Models: Shared Memory vs. Distributed Memory
6. Introduction to OpenMP: Parallelizing Loops and Regions