Skip to content
System Programming
Synchronization

Synchronization and Critical Sections

Overview

This week explores one of the most important challenges in multithreaded programming — synchronization.
Students will understand how race conditions arise when multiple threads access shared data concurrently, and how to control access to critical sections using synchronization primitives such as mutexes, spinlocks, semaphores, condition variables, and atomic operations.



Key Concepts

Concurrency and Race Conditions

  • Race condition: when multiple threads access and modify shared data simultaneously, leading to unpredictable results.
  • Critical section: a segment of code that must not be executed by more than one thread at a time.
  • Data consistency: maintaining integrity of shared resources under concurrent access.

Synchronization Primitives

  • Mutex (Mutual Exclusion Lock):

    • Ensures only one thread executes a critical section at a time.
    • Core functions: pthread_mutex_init, pthread_mutex_lock, pthread_mutex_unlock, pthread_mutex_destroy.
  • Spinlock:

    • Lightweight lock that continuously checks availability without sleeping.
    • Suitable for short critical sections on multicore systems.
  • Semaphore:

    • A counter-based synchronization mechanism for controlling access to a finite number of resources.
    • POSIX functions: sem_init, sem_wait, sem_post, sem_destroy.
  • Condition Variable:

    • Used for signaling between threads.
    • Allows a thread to wait until a certain condition becomes true.
    • Core functions: pthread_cond_wait, pthread_cond_signal, pthread_cond_broadcast.
  • Atomic Operations and CAS (Compare-And-Swap):

    • Low-level operations ensuring atomic updates without explicit locks.
    • Foundation for lock-free synchronization structures.

Deadlocks and Starvation (Intro)

  • Circular wait, hold and wait, no preemption, mutual exclusion — the four conditions for deadlock.
  • Techniques to avoid or detect deadlocks will be discussed in future weeks.

Practice

Demonstrating Race Conditions

  • Create a shared counter updated by multiple threads without locks — observe inconsistent results.
  • Protect the counter using a mutex and verify correct output.

Working with Mutexes

  • Use a mutex to protect a critical section in a multi-threaded program.
  • Experiment with pthread_mutex_trylock and PTHREAD_MUTEX_RECURSIVE.

Using Semaphores

  • Implement a simple producer-consumer synchronization using semaphores.

Condition Variables

  • Demonstrate thread signaling (one thread waits until another signals a condition).
  • Compare busy-waiting vs. condition-based waiting.

Spinlocks and Atomics

  • Experiment with short critical sections using spinlocks.
  • Observe CPU utilization differences compared to mutexes.

Homework


References

Required

Recommended


Quiz (Self-Check)

  1. What causes a race condition?
  2. What is the difference between a mutex and a spinlock?
  3. How does a semaphore differ from a mutex?
  4. What are the typical use cases for condition variables?
  5. What does “atomic operation” mean?
  6. Name the four necessary conditions for a deadlock.
  7. Why is it important to minimize the time spent inside a critical section?