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(即將介紹)才是正確的原語。