8.7. Events

asyncio.Event is het eenvoudigste signaleringsprimitief dat de module biedt. Eén coroutine stelt een event in wanneer er iets is gebeurd; een willekeurig aantal andere coroutines wacht tot het event is ingesteld voordat ze verdergaan. Er is geen payload – een event is een boolean die aanspringt en aan blijft totdat iets het wist.

8.7.1. De basisvorm

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

De wachtende taak draait, bereikt await evt.wait() en wordt gepauzeerd – het event begint in de gewiste status, dus de wait-aanroep geeft de controle terug aan de loop. Een seconde later roept main evt.set() aan en wordt de wachter ingepland om te hervatten. De afsluitende await asyncio.sleep(0) is slechts een yield zodat de loop de kans krijgt de wachter te draaien voordat main terugkeert.

8.7.2. De vier methoden

  • set() – zet het event op ingesteld en plant elke coroutine die momenteel op wait() is geblokkeerd in om te hervatten. Het event blijft ingesteld totdat het gewist wordt.

  • clear() – zet het event terug op gewist. Coroutines die daarna await wait() aanroepen, zullen weer blokkeren.

  • wait() – een coroutine. Als het event al is ingesteld, keert deze direct terug. Anders blokkeert hij totdat iets het instelt.

  • is_set() – geeft de huidige status terug zonder te blokkeren. Nuttig wanneer een coroutine het event wil controleren zonder erop te wachten.

8.7.3. Meerdere wachters

Meerdere coroutines kunnen op hetzelfde event awaiten. Wanneer iets set() aanroept, worden ze allemaal ingepland om te hervatten. Het event is standaard één-op-veel; er is geen behoefte aan meerdere events om naar meerdere wachters uit te waaieren.

8.7.4. Een event hergebruiken

Een veelvoorkomend patroon is om een event herhaaldelijk te gebruiken – een coroutine die eenmaal per signaal draait – door het na elke doorgang te wissen:

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

De producent stelt het event in wanneer er werk is; de worker wist het zodra hij het signaal heeft opgepikt. Als de producent het event mogelijk opnieuw instelt voordat de worker is ontwaakt, wil de worker dat geval misschien afhandelen (while go.is_set(): ...) voordat hij wist.

8.7.5. Veilige contexten

Het is niet veilig om set() op Event aan te roepen van binnen een interrupthandler. Het mechanisme dat het gebruikt om wachters in te plannen, gaat ervan uit dat het binnen de eigen context van de event loop draait, wat bij interrupts niet zo is. Om een asyncio-taak vanuit een interrupthandler te wekken, is ThreadSafeFlag (binnenkort behandeld) het juiste primitief.