Code that survives being run at once
Day 153 of 155
๐ Full deep-dive with code examples
The Shared Whiteboard Analogy
Two people keep a tally on one whiteboard. Each does three things: read the number, add one, write it back.
Alice reads 5
Bob reads 5 <- Alice has not written yet
Alice writes 6
Bob writes 6 <- the tally should say 7
An increment vanished. Nobody was careless. The steps just interleaved.
One Line, Three Steps
counter = counter + 1
One move? No, three: read counter, add 1, write it back.
A thread can be paused between any two, and that gap is where a race condition lives.
Here it costs you an increment, which is why this shape is called a lost update.
Races need two ingredients at once: state that is shared (several threads reach it) and mutable (someone writes it).
Remove either and the race usually goes away.
Ways Out, Cheapest First
| Approach | Idea |
|---|---|
| Immutability | Produce new values, don't mutate |
| Confinement | Give each thread its own copy |
| Atomics | Read-modify-write in one go |
| Locks | Admit one thread at a time |
A lock marks a critical section: the stretch where the invariant is temporarily false and nobody else should look.
with lock:
counter = counter + 1
A Lock Is Not a Proof
The lock has to cover the whole invariant, not each line.
with lock:
missing = key not in cache # check
if missing:
value = build(key) # expensive, so keep it out of the lock
with lock:
cache[key] = value # act
Every touch of cache is locked, and the bug survives: two threads can both see missing and both build.
A decision made under one lock is stale the moment that lock is released.
In One Sentence
Thread-safe code keeps its invariants true no matter how the scheduler interleaves the threads running it.
๐ Enjoying these? Follow for daily ELI5 explanations!
Making complex tech concepts simple, one day at a time.
Top comments (0)