8.9. ThreadSafeFlag#
asyncio.ThreadSafeFlag on signalointiprimitiivi, jonka asyncio tarjoaa siihen tapaukseen, jota Event ei tue: keskeytyksenkäsittelijän on herätettävä asyncio-tehtävä. Keskeytys laukeaa tapahtumasilmukan normaalin vuorottamisen ulkopuolella, joten asyncio.Event.set()-metodin tekemää kirjanpitoa ei voida tehdä turvallisesti sen sisältä.
8.9.1. Miksi erillinen primitiivi#
asyncio.Event.set() suorittaa kirjanpidon, joka herättää odottavat korutiinit olettaen, että sitä kutsutaan silmukan parhaillaan ajaman tehtävän sisältä. Keskeytyksenkäsittelijä ei täytä tätä oletusta – se voi laueta minkä tahansa kahden käskyn välillä ja turmella sen, mitä silmukka oli kesken tekemässä.
asyncio.ThreadSafeFlag.set() on kirjoitettu turvalliseksi noissa konteksteissa. Tämän turvallisuuden hintana on pienempi ominaisuusvalikoima kuin Event-luokalla: ThreadSafeFlag-lippua voi odottaa vain yksi tehtävä kerrallaan, ja lippu nollautuu automaattisesti, kun odottava tehtävä herää.
8.9.2. Perusrakenne#
Tyypillinen käyttö on GPIO-keskeytys, joka välittää painikkeen painalluksen tapahtuman asyncio-tehtävälle:
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 suoritetaan keskeytyskontekstissa aina, kun GPIO laukeaa; se ainoastaan kutsuu flag.set()-funktiota. Asyncio-tehtävä watcher suorittaa await-kutsun flag.wait()-funktiolle silmukassa; joka kerta kun lippu asetetaan, tehtävä jatkaa, suorittaa työn, jota painikkeen painallus vaatii, ja palaa sitten silmukassa odottamaan uudelleen.
8.9.3. Kolme metodia#
set()– merkitsee lipun asetetuksi. Jos jokin tehtävä parhaillaan odottaawait()-kutsussa, ajoita se jatkamaan. Turvallinen kutsua keskeytyksenkäsittelijästä.wait()– korutiini. Estää suorituksen, kunnes lippu on asetettu, ja palaa sitten. Lippu tyhjennetään automaattisesti palatessa – seuraavawait-kutsu estää suorituksen taas, kunnes seuraavaset()tapahtuu.clear()– tyhjentää lipun nimenomaisesti odottamatta sitä. Hyödyllinen ennen ensimmäistäwait-kutsua, jotta voidaan hylätä mahdolliset häiriöperäiset aikaisetset()-kutsut, jotka laukesivat ennen kuin tehtävä oli valmis.
8.9.4. Vain yksi odottaja#
Vain yksi tehtävä voi olla wait()-kutsussa kerrallaan. Sovelluksen kannattaa muotoilla tämä yhdeksi omistajatehtäväksi lipulle – yleensä samaksi tehtäväksi, joka asensi keskeytyksenkäsittelijän – ja muut tehtävät kommunikoivat sen kanssa Event-, Lock-objektien tai näiden primitiivien suojaamien jaettujen tietorakenteiden välityksellä.
Kun painikkeen on herätettävä useita tehtäviä, omistajatehtävä on oikea paikka haarauttaa: yllä oleva watcher voisi kutsua some_event.set()-funktiota jokaisen painalluksen jälkeen, ja kaikki some_event-objektia odottavat tehtävät jatkaisivat.