Common Patterns: Bounded Buffer and Resource Pool
Pattern 1: Bounded Buffer
A queue with fixed capacity N. Use TWO semaphores: empty (initialized to N) tracks empty slots, full (initialized to 0) tracks filled slots. Producer waits on empty, signals full. Consumer waits on full, signals empty.
Pattern 2: Resource Pool
A pool of N identical resources (connections, threads, licenses). Single semaphore initialized to N. Thread waits to acquire, signals to release. Simple and elegant.
Why Two Semaphores for Bounded Buffer?
Producers need to wait when buffer is FULL (no empty slots). Consumers need to wait when buffer is EMPTY (no full slots). Two different conditions require two different semaphores. They work together, moving in opposite directions.
Invariant: empty + full = N (always). When producer takes an empty slot, consumer gains a full slot, and vice versa.