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"