8.7. Události#

asyncio.Event je nejjednodušší signalizační primitivum, které modul poskytuje. Jedna korutina událost nastaví, když se něco stalo; libovolný počet jiných korutin čeká, až bude událost nastavena, než budou pokračovat. Není zde žádný náklad (payload) – událost je booleovská hodnota, která se překlopí na zapnuto a zůstane zapnutá, dokud ji něco nevymaže.

8.7.1. Základní tvar#

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

Úloha čekatele běží, narazí na await evt.wait() a je pozastavena – událost začíná ve vymazaném stavu, takže volání wait předá řízení zpět smyčce. O sekundu později main zavolá evt.set() a čekatel je naplánován k obnovení. Závěrečné await asyncio.sleep(0) je jen yield, aby smyčka dostala šanci spustit čekatele předtím, než se main vrátí.

8.7.2. Čtyři metody#

  • set() – překlopí událost na nastaveno a naplánuje obnovení každé korutiny aktuálně blokované na wait(). Událost zůstává nastavena, dokud není vymazána.

  • clear() – překlopí událost zpět na vymazáno. Korutiny, které poté await wait(), budou opět blokovat.

  • wait() – korutina. Pokud je událost již nastavena, vrátí se okamžitě. Jinak blokuje, dokud ji něco nenastaví.

  • is_set() – vrací aktuální stav bez blokování. Užitečné, když chce korutina událost zkontrolovat, aniž by na ni čekala.

8.7.3. Více čekatelů#

Několik korutin může awaitovat na stejnou událost. Když něco zavolá set(), jsou všechny naplánovány k obnovení. Událost je ve výchozím nastavení jeden-k-mnoha; není potřeba mít několik událostí, aby se rozvětvily k několika čekatelům.

8.7.4. Opětovné použití události#

Běžným vzorem je používat událost opakovaně – korutina, která běží jednou na signál – jejím vymazáním po každém průchodu:

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

Producent událost nastaví, kdykoli je práce; pracovník ji vymaže, jakmile signál vyzvedne. Pokud by producent mohl událost nastavit znovu předtím, než se pracovník probudil, může chtít pracovník tento případ ošetřit (while go.is_set(): ...) před vymazáním.

8.7.5. Bezpečné kontexty#

Na Event není bezpečné volat set() zevnitř obslužné rutiny přerušení. Mechanismus, který používá k plánování čekatelů, předpokládá, že běží uvnitř vlastního kontextu smyčky událostí, čímž přerušení nejsou. Pro probuzení asyncio úlohy z obslužné rutiny přerušení je správným primitivem ThreadSafeFlag (probráno zanedlouho).