8.7. Események

Az asyncio.Event a legegyszerűbb jelzési primitív, amelyet a modul kínál. Az egyik korutin beállít egy eseményt, amikor valami megtörtént; tetszőleges számú más korutin várja, hogy az esemény beállításra kerüljön, mielőtt folytatná. Nincs hasznos terhelés – egy esemény egy boolean, amely bekapcsol, és bekapcsolva marad, amíg valami törli.

8.7.1. Az alapforma

import asyncio

async def waiter(evt):
    print("waiting")
    await evt.wait()
    print("got signal")

async def main():
    evt = asyncio.Event()
    asyncio.create_task(waiter(evt))
    await asyncio.sleep(1)
    evt.set()
    await asyncio.sleep(0)

asyncio.run(main())

A váró feladat fut, eléri az await evt.wait()-et, és felfüggesztésre kerül – az esemény törölt állapotban indul, így a wait hívás visszaadja a vezérlést a huroknak. Egy másodperccel később a main meghívja az evt.set()-et, és a váró feladat ütemezésre kerül a folytatáshoz. A záró await asyncio.sleep(0) csak egy yield, hogy a hurok esélyt kapjon a váró feladat futtatására, mielőtt a main visszatér.

8.7.2. A négy metódus

  • set() – beállítja az eseményt, és beütemezi az összes, jelenleg a wait()-en blokkolt korutint a folytatáshoz. Az esemény beállítva marad, amíg nem törlik.

  • clear() – visszaállítja az eseményt törölt állapotba. Azok a korutinok, amelyek ezután await wait()-elnek, ismét blokkolódni fognak.

  • wait() – egy korutin. Ha az esemény már be van állítva, azonnal visszatér. Egyébként blokkol, amíg valami be nem állítja.

  • is_set() – blokkolás nélkül visszaadja az aktuális állapotot. Hasznos, amikor egy korutin ellenőrizni akarja az eseményt anélkül, hogy várna rá.

8.7.3. Több váró

Több korutin is await-elheti ugyanazt az eseményt. Amikor valami meghívja a set()-et, mindegyik ütemezésre kerül a folytatáshoz. Az esemény alapértelmezetten egy-a-többhöz; nincs szükség több eseményre ahhoz, hogy több várónak szétoszoljon.

8.7.4. Egy esemény újrafelhasználása

Gyakori minta, hogy egy eseményt ismételten használnak – egy korutin, amely jelenként egyszer fut – úgy, hogy minden menet után törlik:

async def worker(go):
    while True:
        await go.wait()
        do_one_unit_of_work()
        go.clear()

A termelő beállítja az eseményt, valahányszor van munka; a munkás törli, amint felvette a jelet. Ha a termelő esetleg újra beállítaná az eseményt, mielőtt a munkás felébredt volna, a munkás kezelni szeretheti ezt az esetet (while go.is_set(): ...), mielőtt törölné.

8.7.5. Biztonságos kontextusok

Az Event-en nem biztonságos a set() hívása megszakításkezelőn belülről. A mechanizmus, amelyet a várók ütemezésére használ, feltételezi, hogy az eseményhurok saját kontextusán belül fut, ami a megszakításokra nem igaz. Egy asyncio-feladat megszakításkezelőből való felébresztéséhez az ThreadSafeFlag (amelyet hamarosan tárgyalunk) a megfelelő primitív.