asyncio.Condition需配合Lock使用,用于等待条件成立:wait()自动释放锁并挂起,唤醒后重获锁;notify()无需持锁但应在锁内修改状态后调用;必须用while循环双重检查条件,且条件变量须受锁保护。

asyncio.Condition 的基本用法和核心逻辑
它不是用来替代 asyncio.Lock 的,而是配合锁一起用,解决「等某个条件成立再继续」的问题。本质是:一个协程在锁保护下检查条件,不满足就挂起;另一个协程修改状态后通知等待者。关键点在于——wait() 会自动释放关联的锁,挂起当前协程;被 notify() 唤醒后,会重新获取锁再返回。
-
asyncio.Condition必须传入一个asyncio.Lock(或asyncio.RLock),不能裸用 -
wait()只能在已持有锁的前提下调用,否则抛RuntimeError: await was called on a coroutine with no running event loop或更常见的RuntimeError: cannot wait on a condition not owned by the current task -
notify()和notify_all()不要求当前协程持有锁,但通常应在锁保护下修改完共享状态后再调用,避免竞态
典型错误:忘记在 wait 前加锁,或在 notify 后没改状态
最常踩的坑是写成这样:
cond = asyncio.Condition() # 错误:没加锁就 wait await cond.wait() # RuntimeError
或者:
async def waiter():
async with cond:
while not data_ready:
await cond.wait() # 等待,但 data_ready 永远不会变
async def notifier():
错误:没改 data_ready 就 notify
cond.notify() # 等待者醒来,发现 data_ready 还是 False,又进 wait
正确模式必须是「锁内检查 → 不满足则 wait → wait 返回后再次检查」,即经典的 double-check:
async with cond:
while not data_ready:
await cond.wait()
# 此时 data_ready 为 True,且已持锁notify() 和 notify_all() 的行为差异与适用场景
notify(n=1) 默认只唤醒一个等待协程,适合「生产者-消费者」中单个任务取走一项工作的场景;notify_all() 唤醒全部,适合「状态广播」类需求,比如配置重载完成、服务启动就绪等。
- 唤醒顺序不保证 FIFO,取决于事件循环调度,不要依赖唤醒次序
-
notify(0)是合法调用,但什么也不做;notify(5)在只有 2 个协程等待时,只唤醒 2 个 - 被唤醒的协程不会立刻执行,要等当前持有锁的协程退出
async with块后,才能重新抢到锁并继续
实际使用中容易忽略的复杂点
Condition 本身不保存状态,所有条件判断逻辑(比如 data_ready)必须由你用外部变量或对象属性维护,且读写需在锁保护下进行。这意味着你得自己管理「状态可见性」——Python 的内存模型不保证跨协程的变量修改立即可见,所以不能把条件判断写在锁外。
- 别用全局布尔变量做条件,除非它被封装在锁保护的类里
- 多个 Condition 共享同一把锁是允许的,但每个 Condition 应对应一类独立条件,混用会导致语义混乱
- 如果等待逻辑可能超时,要用
asyncio.wait_for(await cond.wait(), timeout=...),但注意:超时异常抛出时,协程仍处于等待队列中,需确保后续有notify清理,否则造成泄漏










