Advanced

Coordinating two streams

Two streams that must take turns need a signal. An event says a thing happened, and a waiting stream picks up exactly where its turn begins.

What you will see

False

About 2 min of reading

Try It

Check what a flag does.

1

Flag set.

import threading go = threading.Event() go.set() print(go.is_set())

Before you run it, say out loud what it will print. Then open the answer.

What it prints
True
2

Wait returns.

import threading ready = threading.Event() ready.set() print(bool(ready.wait(1)))

Before you run it, say out loud what it will print. Then open the answer.

What it prints
True
3

Order held.

import threading first = threading.Event() second = threading.Event() first.set() first.wait() second.set() print(second.is_set())

Before you run it, say out loud what it will print. Then open the answer.

What it prints
True
4

Cleared flag.

import threading go = threading.Event() go.set() go.clear() print(go.is_set())

Before you run it, say out loud what it will print. Then open the answer.

What it prints
False

Put it in order

Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.

Data Flow

A stage waits on a flag, the other stage sets the flag, the waiting stage resumes, and the flag can be cleared for the next round.
1 a stage waits on a flag
↓
2 the other stage sets it
↓
3 the waiting stage resumes
↓
4 clear it for the next round
Line by line

Read the first example one line at a time. A line that prints shows what appears on screen; the rest set things up quietly.

import threading sets nothing on screen
go = threading.Event() sets nothing on screen
go.set() sets nothing on screen
print(go.is_set()) True
Why this works
Signal, do not guess

A sleep is a guess about timing. A flag states the fact, so the waiting stream starts when the fact is true instead of when the guess says so.

Timeouts must be read

A wait with a timeout can return false. If nobody reads that value, a stalled stage looks like a working one.

Examples

Set and wait

import threading

go = threading.Event()
go.set()
print(go.is_set())

An event is a flag with a wait attached. Setting it releases whoever was waiting.

A turn that waits

import threading

ready = threading.Event()
ready.set()
if ready.wait(1):
    print("go")

wait returns the flag state, so a timeout path is visible in the return value instead of a hang.

Two flags in order

import threading

first = threading.Event()
second = threading.Event()
first.set()
first.wait()
second.set()
print(second.is_set())

Each stage sets the flag the next stage waits for. The order lives in the flags, not in sleeps.

A sleep instead

import time
time.sleep(0.01)
print("maybe")

A sleep guesses the timing. It passes on a fast machine and fails when the machine is slow, which is the wrong fix.

Clear to run again

import threading

go = threading.Event()
go.set()
go.clear()
print(go.is_set())

Clearing an event makes the next round wait again, which is how a repeating stage is driven.

A condition to test

import threading

cond = threading.Condition()
with cond:
    print("owned")

A condition pairs a lock with a wait for a value. Use an event for a flag and a condition for a value.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Threads and the GIL
Previous Lesson
Threads and the GIL
Next Lesson
Two locks, two orders
Two locks, two orders →