flowchart TD A["Barrier(3)"] --> B["thread 1 calls wait() -- parks"] B --> C["thread 2 calls wait() -- parks"] C --> D["thread 3 calls wait()"] D --> E["the party is complete"] E --> F["all three released together"] F --> G["each gets a distinct index; exactly one gets 0"] G --> H["that one can do the once-per-round work"] E --> I["the barrier RESETS for the next round"] J["a thread that never arrives"] --> K["everyone waits forever -- use a timeout"]
What is a Barrier?
A Barrier makes a group of threads wait until all have reached a point.
Use cases:
- start multiple workers at the same time
- multi-step simulations
Example
import threading
import time
barrier = threading.Barrier(3)
def worker(i: int) -> None:
time.sleep(0.2 * i)
print("worker", i, "ready")
barrier.wait()
print("worker", i, "started")
threads = [threading.Thread(target=worker, args=(i,)) for i in range(3)]
for t in threads:
t.start()
for t in threads:
t.join()Notes
- If a thread fails and never reaches the barrier, others can block.
- You can set a timeout in
barrier.wait(timeout=...).
How a Barrier behaves
A Barrier(3) is a meeting point for exactly three threads. Each thread calls barrier.wait() when it arrives. The first two are put to sleep. When the third arrives, all three are released at once and continue past the wait(). Then the barrier resets, so it can be used again for the next round.
Reading the example
Worker i sleeps for 0.2 * i seconds to simulate different amounts of work, then prints ready and calls wait(). Worker 0 is ready almost immediately but has to wait; worker 2 arrives last at about 0.4 seconds. Only then do all three print started, at nearly the same moment. You will see three ready lines spaced out and then three started lines together.
wait() also returns a number from 0 to parties - 1, different for each thread. You can use it to pick one thread to do a special job after the meeting, such as printing a summary.
A multi-step example
Barriers are handy when work happens in phases and no thread may start phase 2 until all have finished phase 1:
import threading
barrier = threading.Barrier(3)
results = [0, 0, 0]
def worker(i: int) -> None:
results[i] = (i + 1) * 10 # phase 1: compute my part
barrier.wait() # wait until everyone has finished phase 1
total = sum(results) # phase 2: safe, all parts are ready
print("worker", i, "sees total", total)
threads = [threading.Thread(target=worker, args=(i,)) for i in range(3)]
for t in threads:
t.start()
for t in threads:
t.join()Every worker prints a total of 60, because none of them reads results until all three have written their part.
Handling failures
If one thread crashes before reaching the barrier, the others would wait forever. Two protections exist. Pass a timeout, as in barrier.wait(timeout=5), and a threading.BrokenBarrierError is raised for everyone when time runs out. Or call barrier.abort() from an error handler to release the waiting threads with that same error.
Common mistakes
- Wrong party count.
Barrier(3)with only two threads waits forever. - No timeout in code where a worker might fail.
- Using it for a one-off signal. If one thread just needs to tell others to go, an
Eventis simpler.
When to use it
Use a Barrier for simulations with steps, parallel algorithms with phases, and tests that need many threads to start at the same instant.
Check yourself
`Barrier(3)`. Two threads have called `wait()`. What are they doing?
Verified: with 2 of 3 arrived, neither had passed. A barrier releases nobody until the party count is reached.
Show answer
B — Both blocked, waiting for the third — Verified: with 2 of 3 arrived, neither had passed. A barrier releases nobody until the party count is reached.
What does `barrier.wait()` return?
Each participant gets a different index. Checking for 0 is the standard way to nominate one thread for once-per-round work without needing another lock.
Show answer
C — A distinct index per thread, exactly one of which is 0 — Each participant gets a different index. Checking for 0 is the standard way to nominate one thread for once-per-round work without needing another lock.
After all threads pass, what state is the barrier in?
A barrier is reusable by design, which is what makes it suited to looping phases — every worker finishes round N before any starts round N+1.
Show answer
B — Reset, ready for the next round — A barrier is reusable by design, which is what makes it suited to looping phases — every worker finishes round N before any starts round N+1.
One participant crashes before reaching the barrier. What happens to the others?
There is no default timeout. Pass one to `wait()` and handle `BrokenBarrierError`, which is what the remaining threads receive once the barrier is broken.
Show answer
B — They wait forever unless a timeout was given — There is no default timeout. Pass one to `wait()` and handle `BrokenBarrierError`, which is what the remaining threads receive once the barrier is broken.
🧪 Try It Yourself
Exercise 1 – Basic Barrier
Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.
Press Run to see what your code prints.Exercise 2 – Barrier Parties Count
Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.
Press Run to see what your code prints.Exercise 3 – Abort a Barrier
Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.
Press Run to see what your code prints.