Skip to content

14/32 · 6 min

Three problems worth knowing by name

These three appear in every syllabus and most interviews, because each isolates one difficulty that real concurrent code runs into.

Producer and consumer

One thread puts items in a bounded buffer, another takes them out. The producer must wait when it is full, the consumer when it is empty.

python
empty = Semaphore(N)      # free slots
full  = Semaphore(0)      # filled slots
mutex = Lock()

def producer(item):
    empty.acquire()       # wait for a free slot
    with mutex:
        buffer.append(item)
    full.release()        # one more item available

def consumer():
    full.acquire()        # wait for an item
    with mutex:
        item = buffer.pop(0)
    empty.release()       # one more free slot
    return item

Two semaphores count the resources and a mutex protects the buffer. Taking them in the wrong order deadlocks: acquire the mutex first, then block on empty, and the consumer can never get in to free a slot.

This is the shape of every queue between a fast and a slow component.

Readers and writers

Many threads may read at once; a writer needs exclusive access.

The naive solution starves writers: while any reader holds the lock more can join, so a steady stream of readers means a writer waits forever. The fix is to block new readers once a writer is waiting, which costs some read throughput to guarantee the writer eventually runs.

That trade is the point of the problem. Most read write locks let you choose, and the default is often reader preferring, which is fine until it is not.

Dining philosophers

Five philosophers, five forks between them, each needs both neighbours' forks to eat.

If all five pick up their left fork simultaneously, every fork is held, every philosopher waits for their right, and nobody ever eats. That is deadlock, and it arises from symmetry rather than from any mistake.

Fixes, each corresponding to breaking one deadlock condition:

FixBreaks
At most four seated at oncehold and wait
Odd philosophers take right firstcircular wait
Pick up both forks or neitherhold and wait
A waiter grants permissioncircular wait

The ordering fix is the one that generalises: number the forks and always take the lower numbered one first. That is exactly the lock ordering rule, and it is what you would actually do in code.