Python Central HubSign in

Python Programming / Python Synchronization

Condition Variables

Use threading.Condition for advanced coordination (wait/notify) when a state changes.

What is a Condition?

A Condition is used when threads need to:

  • wait until a condition becomes true (e.g., “buffer not empty”)
  • notify others when the state changes

It combines:

  • a lock
  • a wait/notify mechanism

Producer-consumer with Condition

Python
import threading
import time
 
items = []
cond = threading.Condition()
 
 
def producer():
    for i in range(5):
        time.sleep(0.2)
        with cond:
            items.append(i)
            print("produced", i)
            cond.notify()  # wake one waiting consumer
 
 
def consumer():
    for _ in range(5):
        with cond:
            while not items:
                cond.wait()  # release lock and wait
            v = items.pop(0)
        print("consumed", v)
 
 
p = threading.Thread(target=producer)
c = threading.Thread(target=consumer)
 
p.start(); c.start()
p.join(); c.join()
sketch wait_for re-checks the condition; wait just believes you p5.js
A waiting thread can be woken before the thing it is waiting for has actually happened -- by a premature notify, or by a spurious wake-up the operating system is allowed to deliver. Measured: with a notify sent while the predicate was still False, the plain wait thread woke and carried straight on with ready still False. The wait_for thread woke, re-checked, found the predicate false and went back to waiting, proceeding only once the value really changed. That re-check is the entire difference.

Why the while loop?

Always re-check the condition after waking:

  • spurious wakeups can happen
  • another consumer might have consumed the item first

The problem a Condition solves

Suppose a consumer thread needs an item that is not there yet. It could check in a loop, again and again, wasting CPU. Or it could sleep for a while and risk reacting late. A Condition gives a better answer: the consumer sleeps until another thread tells it the state has changed, using no CPU while it waits.

How wait and notify work

A condition wraps a lock. Everything happens inside with cond:. Calling cond.wait() does three things atomically: it releases the lock, puts the thread to sleep, and re-acquires the lock when the thread is woken. Releasing the lock is essential, because otherwise the producer could never get in to add an item. cond.notify() wakes one waiting thread and cond.notify_all() wakes them all. The woken thread continues only after the notifier leaves its with block and frees the lock.

Tracing the example

The consumer enters the block and finds items empty, so it waits. Every 0.2 seconds the producer takes the lock, appends an item, prints, and calls notify(). The consumer wakes, pops the item, and leaves the block. The print("consumed", v) sits outside the lock on purpose, so other threads are not held up while printing.

Waiting on a predicate

Because the wait-in-a-loop pattern is so common, Condition has a shortcut, wait_for, which takes a function and keeps waiting until it returns true:

Python
import threading
import time
 
ready = False
cond = threading.Condition()
 
 
def waiter() -> None:
    with cond:
        cond.wait_for(lambda: ready)  # sleeps until ready is True
        print("go!")
 
 
def setter() -> None:
    global ready
    time.sleep(0.3)
    with cond:
        ready = True
        cond.notify_all()
 
 
t1 = threading.Thread(target=waiter)
t2 = threading.Thread(target=setter)
t1.start(); t2.start()
t1.join(); t2.join()

wait_for checks the predicate first, so it also works if ready was already true.

Common mistakes

  • Using if instead of while. After waking, the state may already have been taken by another consumer, so re-check it in a loop or use wait_for.
  • Calling wait or notify without holding the lock. Python raises a RuntimeError.
  • Changing the state without notifying. The waiter sleeps forever.
  • Notifying before the state changes. Change the data first, then notify.

When to use it

Use a Condition when threads must wait for a state to become true, such as a buffer having room or a flag being set. If you only need a simple producer-consumer hand-off, queue.Queue does all of this for you and is usually the better choice.

🧪 Try It Yourself

Exercise 1 – Condition notify and wait

exerciseYour turn

Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.

Output
Press Run to see what your code prints.

Exercise 2 – notify_all

exerciseYour turn

Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.

Output
Press Run to see what your code prints.

Exercise 3 – Predicate Wait

exerciseYour turn

Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.

Output
Press Run to see what your code prints.

Was this page helpful?

Let us know how we did