8.7. 事件¶
asyncio.Event 是该模块所提供的最简单的信号原语。当某件事发生时,一个协程 设置 一个事件;任意数量的其他协程在继续之前 等待 该事件被设置。它没有载荷——一个事件就是一个布尔值,被翻转为开启状态后会一直保持开启,直到有什么东西清除它。
8.7.1. 基本形式¶
import asyncio
async def waiter(evt):
print("waiting")
await evt.wait()
print("got signal")
async def main():
evt = asyncio.Event()
asyncio.create_task(waiter(evt))
await asyncio.sleep(1)
evt.set()
await asyncio.sleep(0)
asyncio.run(main())
等待者任务运行,遇到 await evt.wait(),随即被挂起——事件起始处于已清除状态,因此 wait 调用会让出控制权回到循环。一秒钟后 main 调用 evt.set(),等待者随即被调度恢复。末尾的 await asyncio.sleep(0) 只是一次让出,好让循环在 main 返回之前有机会运行等待者。
8.7.2. 四个方法¶
8.7.3. 多个等待者¶
多个协程可以 await 同一个事件。当有什么东西调用 set() 时,它们 全部 会被调度恢复。事件默认是一对多的;无需为了让信号扇出到多个等待者而设置多个事件。
8.7.4. 重用事件¶
一种常见的模式是反复使用一个事件——一个每收到一次信号就运行一次的协程——做法是在每一轮之后清除它:
async def worker(go):
while True:
await go.wait()
do_one_unit_of_work()
go.clear()
生产者每当有工作时就设置该事件;工作者一旦取走了信号就清除它。如果生产者可能在工作者被唤醒之前再次设置事件,工作者可能需要在清除之前处理这种情况(while go.is_set(): ...)。
8.7.5. 安全的上下文¶
从中断处理程序内部对 Event 调用 set() 是 不 安全的。它用于调度等待者的机制假定自己运行在事件循环本身的上下文中,而中断并非如此。要从中断处理程序中唤醒一个 asyncio 任务,ThreadSafeFlag(稍后介绍)才是正确的原语。