asyncio.Event 是协程间轻量级状态通知信号,非线程安全锁;与 threading.Event 行为相似但底层隔离,不可混用,必须在 async 函数中 await event.wait(),且需注意 clear() 否则无法触发下一轮通知。

asyncio.Event 是什么,和 threading.Event 有什么区别
它不是线程安全的“锁”,而是协程间轻量级的状态通知信号:一个协程 set(),其他协程在 wait() 就能立刻被唤醒。和 threading.Event 名字像、行为像,但底层完全不共享状态——不能混用,也不能在同步代码里 await 它。
常见错误现象:RuntimeWarning: coroutine 'Event.wait' was never awaited,本质是忘了加 await;或者误把 event.wait 当成函数直接调,结果传了个协程对象进去没执行。
- 必须在 async 函数里用
await event.wait(),不能在普通函数或同步上下文里 await -
event.is_set()是同步方法,可直接调,但返回值反映的是「上一次 set/clear 的快照」,不是实时监听 - 没有超时参数,要带超时得套
asyncio.wait_for(event.wait(), timeout=...)
怎么让多个协程等同一个事件触发后才继续
典型场景:启动一堆 worker 协程,但它们得等初始化完成(比如配置加载完毕)才能开始干活。这时主协程负责 set(),worker 全部 await event.wait() 即可。
注意:Event 是“发令枪”,不是“计数器”。它只管“有没有被 set 过”,不管多少协程在等、也不管 set 多少次(重复 set() 没副作用)。
立即学习“Python免费学习笔记(深入)”;
- 初始化时默认未 set,所有
await event.wait()会挂起,直到第一次event.set() - 一旦
set()过,后续所有await event.wait()立刻返回(不阻塞),直到你手动event.clear() - 如果忘了
clear(),后续协程就收不到“下一轮”通知了——这是最常踩的坑
示例:
import asyncio
<p>async def worker(name, event):
print(f"{name}: waiting...")
await event.wait() # 这里挂起
print(f"{name}: go!")</p><p>async def main():
event = asyncio.Event()
tasks = [worker(f"worker-{i}", event) for i in range(3)]</p><h1>启动所有 worker,但都卡在 wait 上</h1><pre class='brush:python;toolbar:false;'>asyncio.create_task(asyncio.gather(*tasks))
await asyncio.sleep(0.1) # 模拟初始化耗时
print("init done, setting event")
event.set() # 所有 worker 同时被唤醒
await asyncio.sleep(0.1)asyncio.run(main())
如何避免 wait 被永久挂起(死等)
没 set(),await event.wait() 就永远不往下走——不像 asyncio.Queue.get() 至少还能被 cancel。一旦漏掉 set() 或逻辑分支跳过,整个协程就卡死,还可能拖垮整个 event loop。
- 务必确保
set()在某个确定路径中被执行,不要依赖“理论上总会走到” - 敏感逻辑建议加超时:
await asyncio.wait_for(event.wait(), timeout=5.0),捕获asyncio.TimeoutError做降级处理 - 别在
except块里吞掉异常后忘记set(),比如初始化失败时本该通知失败,却让所有 worker 干等 - 调试时可用
event.is_set()打印状态,但注意它不阻塞,只是查快照
和 asyncio.Condition / asyncio.Queue 比,什么时候该选 Event
Event 最适合「单次广播通知」:比如服务启动完成、配置热更生效、外部信号到达。它比 Condition 轻(不用配 lock),比 Queue 简(不传数据、不关心顺序)。
容易混淆的点:有人想用 Event 传递数据(比如“通知+附带错误码”),这不行——它只管“是/否”,要传数据就得搭配变量或用 Queue.put_nowait(...)。
- 需要“等某个条件成立,且可能多次触发” → 用
asyncio.Condition - 需要“发消息 + 数据 + 可能有多个消费者” → 用
asyncio.Queue - 只需要“所有人听到一声哨响就开始跑” →
asyncio.Event刚好,不多不少
复杂点在于:Event 本身不提供清理机制,clear() 得你自己记得调;而且它不记录谁等过、等了多久,调试时看不到等待中的协程列表——这些都得靠日志或额外状态管理补足。










