Skip to content
Optimal Lab

Operating systems · interactive

Producer–consumer

A producer thread makes items and a consumer thread uses them, through a shared buffer with 3 slots. The producer is faster. Without coordination they overwrite and misread; with two semaphores and a lock, each waits its turn.

Producer
···
Consumer

0 corrupted operations so far

Empty buffer with 3 slots. Semaphores: empty = 3, full = 0.

Coordination

tick 0 of 12

Step 2 of 4

  1. 1

    A shared, bounded buffer

    It decouples the two speeds, but both threads touch the same slots and counters.

    Logging, job queues, pipelines.

  2. 2

    Overwrites and empty reads

    A full buffer gets overwritten; an empty one gets read. Unsynchronised index updates can also race.

    Busy-waiting instead burns CPU.

  3. 3

    Two semaphores and a lock

    empty counts free slots, full counts items. Producers wait(empty) then signal(full); consumers the reverse. A mutex protects the indexes.

    wait(empty); lock; put; unlock; signal(full)

  4. 4

    Use a blocking queue

    Languages ship this pattern ready-made: Java's BlockingQueue, Go's buffered channels, Python's queue.Queue.

    A full queue is backpressure.

In production

Tools and technologies

Blocking queues implement this pattern for you.

Not tied to one language — use these names when you search docs or design a system.