Semaphore

  • Has a counter

    • Non negative integer
  • acquire()

    • Decrements the counter
  • release()

    • Increments the counter
  • The counter can be zero

    • acquire() will block
    • Until the counter becomes positive again

Simple Semaphore Implementation


// Increments the counter
void release() {
  // Use a mutex for thread safety
  std::lock_guard<std::mutex> lock(mtx);
  
  // Put the marble in the jar
  ++counter;

  // Use a condition variable to coordinate the threads
  cv.notify_all();
}

// Decrements the counter
void acquire() {
  std::lock_guard<std::mutex> lock(mtx);
  
  // Make sure there is at least one marble in the jar
  while (counter == 0) { cv.wait(lock); }

  // Remove the marble from the jar
  --counter;
}

Binary Semaphore as Mutex

  • The counter can only have two values

    • 0 and 1
  • Used for mutual exclusion

  • To "lock" it, call acquire()

    • The counter is decremented to 0
    • Other threads that call acquire() will be blocked
  • To "unlock" it, call release() in the same thread

    • The counter is incremented to 1
    • One of the other threads that called acquire() can now continue

Binary Semaphore as Condition Variable

  • Also used for signaling

    • Can be used as a replacement for condition variables
  • To wait for a signal, a thread calls acquire()

    • The counter is decremented to 0
    • The thread waits for another thread to increment it
  • To notify a waiting thread, call release()

    • The counter is incremented to 1
    • The waiting thread can now continue
  • To notify multiple threads, use a suitable value for max_count

Semaphores

  • More flexible

    • Can notify any given number of waiting threads
  • Simpler code

    • Avoids working with mutexes and condition variables
  • Performance

    • Can often be faster
  • More versatile

    • Can be used to create more complex synchronization objects
    • Check out "Little Book of Semaphores"