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 odottaa wait()-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 – seuraava wait-kutsu estää suorituksen taas, kunnes seuraava set() tapahtuu.

  • clear() – tyhjentää lipun nimenomaisesti odottamatta sitä. Hyödyllinen ennen ensimmäistä wait-kutsua, jotta voidaan hylätä mahdolliset häiriöperäiset aikaiset set()-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.