288. std::sync::Barrier — Make All Threads Start Phase 2 Together
Your workers finish phase 1 at different speeds, and the fast ones charge into phase 2 while the slow ones are still writing. The hand-rolled fix is an atomic counter and a spin loop — std already ships the real thing.
The hack everyone writes first: bump an AtomicUsize when done, then spin until it hits n. It burns CPU while waiting and resetting it for the next round is a race of its own.
std::sync::Barrier (stable since 1.0) is that counter done right. Barrier::new(n) sets the threshold; every thread that calls .wait() blocks until the n-th one arrives, then all of them are released at once:
| |
With scoped threads a shadowed &barrier is all the sharing you need — no Arc. (The move is for id; the reference moves with it.) Under plain thread::spawn, wrap the barrier in Arc as usual.
Two details the DIY counter doesn’t give you. First, .wait() returns a BarrierWaitResult, and exactly one thread per round gets is_leader() == true — a built-in election for “someone swap the buffers before the next phase”:
| |
Second, the barrier resets itself after releasing n threads. Call .wait() in a loop and you get phase-synchronized rounds for free — the classic shape of iterative simulations: compute a step, wait, leader swaps front/back buffers, wait, repeat.
One trap: the count is fixed at construction. If a thread panics before reaching .wait(), the rest block forever — there’s no timeout variant. Keep the work between barriers panic-free, or use channels when workers can drop out.