Synchronization

the producer–consumer problem and a bounded buffer

Imagine a sushi conveyor belt. Chefs (producers) place plates on the belt; diners (consumers) take plates off. The belt holds only so many plates. If the belt is full, a chef must wait before adding more; if the belt is empty, a diner must wait for a plate to appear. The producer-consumer problem is this exact coordination, and the belt is a bounded buffer — a fixed-size shared queue that producers fill and consumers drain. It is the canonical example that ties together everything in this field.

The bounded buffer is usually a fixed-size array used as a ring (a circular buffer) with a head index, a tail index, and a count of how many items it holds. Three pieces of synchronization keep it correct. One mutex protects the buffer's indices and count so producers and consumers never corrupt them. One condition variable, 'not full', lets a producer wait when count == capacity, and is signalled by a consumer after it removes an item. A second condition variable, 'not empty', lets a consumer wait when count == 0, and is signalled by a producer after it adds an item. Each side takes the lock, waits in a while loop until its condition holds, does its single operation, signals the other side, and unlocks. Built with two counting semaphores instead, 'empty slots' starts at capacity and 'full slots' at zero: a producer waits on empty and posts full; a consumer waits on full and posts empty.

This pattern is everywhere: a thread pool's task queue, a logging thread draining log messages, a network server handing connections to workers, a pipeline stage feeding the next. Getting it right exercises the mutex (protect the shared indices), the condition variable or semaphore (block when there is nothing to do, instead of busy-waiting), and the wait-while-predicate discipline. The classic mistakes are forgetting the while loop (an if breaks under spurious wakeups and multiple consumers) and signalling the wrong condition variable.

Producer: lock(m); while (count == cap) cond_wait(&notFull, &m); buf[tail]=x; tail=(tail+1)%cap; count++; cond_signal(&notEmpty); unlock(m);. The consumer mirrors it, waiting on notEmpty and signalling notFull.

One ring buffer, one mutex, two condition variables: 'not full' for producers, 'not empty' for consumers.

With more than one producer or consumer you must re-check the predicate in a while loop after waking — between the signal and re-acquiring the lock another thread may have already taken the slot. A single shared condition variable signalled with cond_signal can also wake the wrong kind of thread, so signal the specific condition or use broadcast carefully.

Also called
bounded-buffer problemproducer/consumer生產者-消費者問題限界緩衝區