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.
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 itemTwo 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:
| Fix | Breaks |
|---|---|
| At most four seated at once | hold and wait |
| Odd philosophers take right first | circular wait |
| Pick up both forks or neither | hold and wait |
| A waiter grants permission | circular 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.