The pattern
One or more producers create items and place them in a shared buffer. One or more consumers remove items and process them. The two sides run at different speeds, so the buffer absorbs bursts.
The synchronisation
Three things must be coordinated:
- Mutual exclusion protects the buffer so two threads never modify it at once.
- A producer blocks when the buffer is full.
- A consumer blocks when the buffer is empty.
A common implementation uses a mutex plus two condition variables or counting semaphores, one counting empty slots and one counting filled slots.
Why signalling matters
If a consumer just spins checking whether the buffer is empty it wastes CPU. Condition variables let it sleep until a producer signals that an item is ready, and the reverse for a full buffer. Missing a signal or signalling without holding the lock leads to lost wakeups, so the wait must always recheck the condition in a loop.
Key idea
Producer consumer decouples speed mismatches through a shared buffer where producers wait when full and consumers wait when empty, coordinated by signals.