Differences and Applications of Semaphore and Mutex

Differences and Applications of Semaphore and Mutex

Description
Semaphore and Mutex are core tools in operating systems and multithreaded programming for synchronization and mutual exclusion. Their main differences are:

  • Mutex: Used for mutual exclusion access, ensuring only one thread can enter the critical section at a time (e.g., modifying a shared variable).
  • Semaphore: Essentially a counter that can control the concurrency number of multiple threads (e.g., limiting the number of threads accessing a resource).
    Common interview questions require explaining the differences between the two and providing examples of application scenarios.

Problem-Solving Process

  1. Core Concept Understanding

    • Mutex:
      • Only has "locked" and "unlocked" states.
      • Who locks it, who unlocks it (ownership concept).
      • For example, after Thread A locks it, other threads must wait for A to unlock before entering.
    • Semaphore:
      • Is an integer counter, modified via wait() (P operation) and signal() (V operation).
      • No ownership restrictions; any thread can execute signal().
      • Classification:
        • Binary Semaphore (value range 0/1): Functionally similar to a mutex but without ownership.
        • Counting Semaphore (value ≥ 0): Controls access to N resources of the same type.
  2. Key Differences Comparison

    Difference Mutex Semaphore
    Core Purpose Mutual Exclusion (one-on-one competition) Synchronization (controlling multi-thread collaboration)
    Counter Feature No counter, only binary state Has a counter, can represent resource quantity
    Ownership Yes (lock holder must unlock) No (any thread can operate the semaphore)
    Typical Scenario Protecting critical sections (e.g., shared variables) Rate limiting, task scheduling (e.g., thread pool)
  3. Examples

    • Mutex Scenario:
      Multiple threads writing to the same file simultaneously require a mutex to ensure only one thread writes at any moment.
      lock = threading.Lock()
      lock.acquire()   # Lock
      # File writing operation (critical section)
      lock.release()   # Unlock
      
    • Semaphore Scenario:
      A database connection pool has 10 connections, requiring limitation on the number of threads accessing simultaneously.
      semaphore = threading.Semaphore(10)  # Initial value=10
      semaphore.acquire()  # Acquire connection (counter -1)
      # Use database connection
      semaphore.release()  # Release connection (counter +1)
      
  4. Common Misconceptions Clarified

    • Binary Semaphore ≠ Mutex:
      • Mutex has priority inheritance (prevents priority inversion); binary semaphore lacks this mechanism.
      • Mutex must be unlocked by its holder; binary semaphore can be released by other threads.
    • Selection Principle:
      • If strict mutual exclusion is needed (e.g., modifying global variables), use a mutex.
      • If coordinating multiple tasks is needed (e.g., producer-consumer), use a semaphore.
  5. Extension: Underlying Implementation of Semaphore
    Semaphore is implemented via atomic operations (e.g., CAS) and thread blocking queues:

    • When wait() is executed, if counter > 0, decrement by 1 and continue; otherwise, the thread blocks and joins the queue.
    • When signal() is executed, the counter increments by 1, and one blocked thread in the queue is awakened.

Summary
Semaphore is a more general synchronization tool (can implement mutex), but mutex is safer due to its ownership feature. In actual development, choose based on the scenario: use mutex for resource exclusivity, and semaphore for flow control or collaborative logic.