8.9. ThreadSafeFlag¶
asyncio.ThreadSafeFlag je signalizační primitiv, který asyncio poskytuje pro případ, jejž Event nepodporuje: obsluha přerušení potřebuje probudit asyncio úlohu. Přerušení nastane mimo běžné plánování smyčky událostí, takže evidenci, kterou provádí asyncio.Event.set(), nelze z jejího nitra bezpečně vykonat.
8.9.1. Proč samostatný primitiv¶
asyncio.Event.set() provádí evidenci, která probouzí čekající korutiny, za předpokladu, že je volána zevnitř úlohy, kterou smyčka právě běží. Obsluha přerušení tento předpoklad nesplňuje – může nastat mezi libovolnými dvěma instrukcemi a poškodit cokoli, co měla smyčka rozdělané.
asyncio.ThreadSafeFlag.set() je napsána tak, aby byla v takových kontextech bezpečná. Kompromisem za tuto bezpečnost je menší sada funkcí oproti Event: na ThreadSafeFlag může čekat vždy jen jedna úloha v daném okamžiku a příznak se po probuzení čekající úlohy automaticky resetuje.
8.9.2. Základní tvar¶
Typickým použitím je přerušení GPIO, které předává událost stisknutí tlačítka asyncio úloze:
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())
on_press běží v kontextu přerušení pokaždé, když se GPIO spustí; vše, co dělá, je volání flag.set(). Asyncio úloha watcher ve smyčce volá await na flag.wait(); pokaždé, když je příznak nastaven, úloha pokračuje, vykoná veškerou práci, kterou stisknutí tlačítka vyžaduje, a poté se vrátí zpět do čekání.
8.9.3. Tři metody¶
set()– označí příznak jako nastavený. Pokud úloha právě čeká vwait(), naplánuje její pokračování. Bezpečné volat z obsluhy přerušení.wait()– korutina. Blokuje, dokud není příznak nastaven, a poté se vrátí. Příznak se při návratu automaticky vymaže – další voláníwaitbude opět blokovat až do dalšíhoset().clear()– explicitně vymaže příznak bez čekání na něj. Užitečné před prvnímwaitk zahození jakéhokoli falešného předčasnéhoset(), který se spustil dříve, než byla úloha připravena.
8.9.4. Pouze jeden čekající¶
V wait() může být v daném okamžiku pouze jedna úloha. Tvar, který aplikace chce, je jediná vlastnící úloha příznaku – obvykle tatáž úloha, která nainstalovala obsluhu přerušení – přičemž ostatní úlohy s ní komunikují prostřednictvím Event, Lock nebo sdílených datových struktur chráněných těmito primitivy.
Když tlačítko musí probudit několik úloh, je vlastnící úloha místem, odkud se rozvětvit: výše uvedený watcher by mohl po každém stisknutí zavolat some_event.set() a všechny úlohy čekající na some_event by pokračovaly.