8.9. ThreadSafeFlag¶
asyncio.ThreadSafeFlag 是 asyncio 為 Event 不 支援的情況所提供的訊號傳遞原語:中斷處理常式需要喚醒某個 asyncio 任務。中斷會在事件迴圈正常排程之外觸發,因此 asyncio.Event.set() 所做的記帳工作無法從中斷內部安全地完成。
8.9.1. 為何需要獨立的原語¶
asyncio.Event.set() 執行喚醒等待協程的記帳工作時,是假設 它是從事件迴圈當前正在執行的某個任務內部被呼叫的。中斷處理常式並不滿足這個假設——它可能在任意兩條指令之間觸發,並破壞事件迴圈正進行到一半的任何工作。
asyncio.ThreadSafeFlag.set() 的撰寫方式可在那些情境下安全使用。為換取這份安全性,其功能集比 Event 更精簡:一個 ThreadSafeFlag 同一時間只能由 一個 任務等待,且當等待中的任務被喚醒時旗標會自動重設。
8.9.2. 基本形式¶
典型用途是由 GPIO 中斷將按鈕按下事件交付給某個 asyncio 任務::
import asyncio
from machine import Pin
flag = asyncio.ThreadSafeFlag()
def on_press(pin):
flag.set()
async def watcher():
button = Pin("P1", Pin.IN, Pin.PULL_UP)
button.irq(handler=on_press, trigger=Pin.IRQ_FALLING)
while True:
await flag.wait()
print("button pressed")
asyncio.run(watcher())
每當 GPIO 觸發時,on_press 會在中斷情境下執行;它所做的只是呼叫 flag.set()。asyncio 任務 watcher 在迴圈中 await flag.wait();每當旗標被設定,該任務便恢復執行,處理按鈕按下所需的任何工作,然後再回到迴圈頂端繼續等待。
8.9.3. 三個方法¶
8.9.4. 僅限單一等待者¶
同一時間只能有一個任務處於 wait() 中。應用程式想要的形式是為該旗標設置一個單一的 擁有者 任務——通常就是安裝中斷處理常式的那個任務——其他任務則透過 Event、Lock 或由這些原語保護的共享資料結構與它溝通。
當一個按鈕必須喚醒多個任務時,擁有者任務就是進行扇出的地方:上面的 watcher 可以在每次按下後呼叫 some_event.set(),而所有等待 some_event 的任務便會恢復執行。