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.