Semaphore vs Mutex
While a mutex is used for mutual exclusion (only one thread can access a resource), a Semaphore is used for signaling and controlling access to a pool of resources.
Think of a mutex as a bathroom key (only one person can hold it). Think of a semaphore as a bouncer at a club with a strict capacity limit.
Counting Semaphore
A counting semaphore maintains an internal counter.
- When a thread wants to access a resource, it "waits" (or "decrements") the semaphore. If the counter is greater than zero, it decrements the counter and proceeds. If the counter is zero, the thread blocks until another thread increments it.
- When a thread finishes with a resource, it "posts" (or "increments") the semaphore, potentially waking up a blocked thread.
This is highly useful for producer-consumer problems, such as a bounded buffer queue where you want to allow up to N items to be processed concurrently.
Binary Semaphore
A binary semaphore is a counting semaphore whose value is restricted to 0 or 1. It is similar to a mutex, but with a crucial difference: a mutex must be unlocked by the same thread that locked it. A binary semaphore can be posted (incremented) by a different thread than the one that waited on it. This makes binary semaphores ideal for signaling between two distinct threads (e.g., Thread A completes a task and signals Thread B to wake up).
POSIX Semaphore API (sem_post and sem_wait)
The standard POSIX C API for semaphores revolves around sem_t.
#include <semaphore.h>
sem_t available_buffers;
// Initialize semaphore: pointer, shared between processes (0 = no), initial value
sem_init(&available_buffers, 0, 5);
void consumer_thread() {
// Decrement. Blocks if value is 0.
sem_wait(&available_buffers);
// Consume a buffer...
}
void producer_thread() {
// Produce a buffer...
// Increment. Wakes up a waiting consumer if any.
sem_post(&available_buffers);
}
Use Cases in Android: Buffer Counting
A classic use case in the Android multimedia framework (e.g., SurfaceFlinger or camera pipelines) is managing graphics buffers.
When a producer (like an app rendering UI) wants to draw, it needs an empty buffer. If the consumer (SurfaceFlinger) hasn't finished compositing the previous frames, there might be no empty buffers.
A counting semaphore is used where the initial count is the total number of buffers (e.g., 3 for triple-buffering).
- The producer calls
sem_wait()to acquire a buffer. - The consumer calls
sem_post()when it has finished displaying a buffer and it is ready to be reused.