8.9. ThreadSafeFlag

Az asyncio.ThreadSafeFlag az a jelzési primitív, amelyet az asyncio arra az esetre biztosít, amelyet az Event nem támogat: egy megszakításkezelőnek fel kell ébresztenie egy asyncio feladatot. A megszakítás az eseményhurok normál ütemezésén kívül lép működésbe, így az a könyvelés, amelyet az asyncio.Event.set() végez, nem hajtható végre biztonságosan belőle.

8.9.1. Miért külön primitív

Az asyncio.Event.set() azt a könyvelést futtatja, amely felébreszti a várakozó korutinokat, azzal a feltételezéssel, hogy egy olyan feladaton belülről hívják, amelyet a ciklus éppen futtat. Egy megszakításkezelő nem felel meg ennek a feltételezésnek – bármely két utasítás között működésbe léphet, és megronthatja azt, amivel a ciklus éppen félúton volt.

Az asyncio.ThreadSafeFlag.set() úgy van megírva, hogy biztonságos legyen ilyen kontextusokban. Ennek a biztonságnak az ára egy kisebb funkciókészlet, mint az Event esetén: egy ThreadSafeFlag-re egyszerre csak egy feladat várakozhat, és a jelző automatikusan visszaáll, amikor a várakozó feladat felébred.

8.9.2. Az alapvető alak

Egy tipikus felhasználás egy GPIO megszakítás, amely egy gombnyomás eseményt ad át egy asyncio feladatnak:

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())

Az on_press megszakítási kontextusban fut minden alkalommal, amikor a GPIO működésbe lép; mindössze annyit tesz, hogy meghívja a flag.set() függvényt. A watcher asyncio feladat egy ciklusban await-ol a flag.wait() hívásra; minden alkalommal, amikor a jelzőt beállítják, a feladat folytatódik, elvégzi azt a munkát, amit a gombnyomás megkövetel, majd visszatér a ciklusba újabb várakozásra.

8.9.3. A három metódus

  • set() – a jelző beállítottként jelölése. Ha egy feladat éppen a wait() hívásban várakozik, ütemezi annak folytatását. Biztonságosan hívható egy megszakításkezelőből.

  • wait() – egy korutin. Addig blokkol, amíg a jelzőt be nem állítják, majd visszatér. A jelző visszatéréskor automatikusan törlődik – a wait következő hívása ismét blokkolni fog a következő set() hívásig.

  • clear() – a jelző explicit törlése anélkül, hogy várakoznánk rá. Hasznos az első wait előtt, hogy elvessünk bármely hamis korai set() hívást, amely azelőtt lépett működésbe, hogy a feladat készen állt volna.

8.9.4. Csak egy várakozó

Egyszerre csak egy feladat lehet a wait() hívásban. Az az alak, amelyet egy alkalmazás szeretne, egyetlen tulajdonos feladat a jelzőhöz – általában ugyanaz a feladat, amely telepítette a megszakításkezelőt –, miközben más feladatok Event, Lock vagy az ezek a primitívek által védett megosztott adatszerkezetek révén kommunikálnak vele.

Amikor egy gombnak több feladatot kell felébresztenie, a tulajdonos feladat az a hely, ahol szétoszthat: a fenti watcher minden nyomás után meghívhatná a some_event.set() függvényt, és a some_event hívásra várakozó összes feladat folytatódna.