asyncio Condition wait notify pattern in Python
Coordinate coroutines with asyncio.Condition: workers wait for notifications and the main task notifies one or all of them.
Python code
31 linesimport asyncio
async def worker(condition, name):
async with condition:
print(f"{name} waiting...")
await condition.wait()
print(f"{name} notified!")
async def main():
condition = asyncio.Condition()
tasks = [asyncio.create_task(worker(condition, f"worker-{i}")) for i in range(3)]
await asyncio.sleep(0.1) # let workers acquire the condition and wait
async with condition:
print("notifying one worker...")
condition.notify(1)
await asyncio.sleep(0.1)
async with condition:
print("notifying all workers...")
condition.notify_all()
await asyncio.gather(*tasks)
if __name__ == "__main__":
asyncio.run(main())
Output
worker-0 waiting...
worker-1 waiting...
worker-2 waiting...
notifying one worker...
worker-0 notified!
notifying all workers...
worker-1 notified!
worker-2 notified!
How it works
The asyncio.Condition provides an async context manager that acquires the associated lock before allowing await condition.wait(). When wait() is called, the coroutine releases the lock and suspends until notify() or notify_all() is called. The notify(1) wakes up one waiting coroutine, while notify_all() wakes all. Because the workers are created with create_task, they run concurrently, and the explicit asyncio.sleep calls in main give them time to start waiting before notifications occur. This pattern is useful for signaling between coroutines without busy-waiting.
Common mistakes
- Calling `condition.wait()` outside the `async with condition:` block, causing a RuntimeError.
- Forgetting to `await` the `condition.wait()` call, suspending the coroutine incorrectly.
- Not using `async with` when calling `notify()` or `notify_all()`, leading to a missing lock context.
- Assuming notifications are queued; if no waiter is present, the notification is lost.
Variations
- Use `asyncio.Event` for simpler broadcast signaling when you don't need per-condition locking.
- Use `asyncio.Queue` to explicitly pass values between producer and consumer coroutines.
Real-world use cases
- Coordinating multiple background workers that should pause until a resource becomes available.
- Implementing a publisher-subscriber pattern where one task notifies several consumers of new data.
- Synchronizing startup of parallel services in a simulator or test harness before proceeding.
Sponsored
More from Concurrency & performance
- Benchmark list.append vs deque.append in Python medium
- Build a Python Performance Profiler That Generates Readable Reports medium
- Graceful Shutdown Executor Context Manager in Python medium
- How to Build a Producer-Consumer Pattern with asyncio.Queue in Python medium
- How to Cancel an asyncio Task with Graceful Cleanup in Python medium
- How to Convert Data in Parallel with ThreadPoolExecutor in Python easy
Keep learning
Related tutorials and quizzes for this topic.