2.32. Korutinok

A korutin olyan függvény, amely félúton megszakíthatja a futását, majd később onnan folytatja, ahol abbahagyta, az összes lokális változójával érintetlenül. A generátor mintát tükrözi – egy függvénytörzset, amelyet fel lehet függeszteni és folytatni – egyetlen változással abban, hogy ki vezérli az egyes folytatásokat.

A korutin írásához a Python szintaxisa az async / await kulcsszópár. Az async korutinként jelöli meg a függvényt; az await azokat a pontokat jelöli meg benne, ahol a megszakítás megengedett.

2.32.1. Korutin definiálása

Az async def kulcsszóval definiált függvény egy korutin függvény. A meghívása nem futtatja le a törzset; egy korutin objektumot ad vissza, amely még nem indult el:

async def greet(name):
    print("hello,", name)

coro = greet("Alice")        # body NOT run yet

A korutin objektum a függvény legelején van felfüggesztve. Valaminek vezérelnie kell, hogy a törzs lefusson – ez a valami egy eseményhurok, egy futásidejű komponens. A MicroPython az asyncio modulban szállít egyet. Egyelőre tekintsd a korutint úgy, hogy „futásra kész, vezérlőre vár”.

2.32.2. Megszakítás egy korutinon belül

Egy korutinon belül egy await kifejezés felfüggeszti a végrehajtást, amíg a várt érték készen nem áll:

async def fetch_and_log():
    data = await read_sensor()
    print("got:", data)

Amikor a törzs eléri az await read_sensor() pontot, a korutin visszaadja a vezérlést annak, ami éppen futtatja. Amikor a read_sensor() befejeződik, a vezérlő a következő soron folytatja a korutint, az eredményt a data változóhoz kötve.

Az await csak egy korutinon belül érvényes. Egy közönséges függvényben való használata szintaktikai hiba.

2.32.3. Kapcsolat a generátorokkal

A korutinok és a generátorok ugyanazon az alapmechanizmuson osztoznak. A különbség abban van, hogy ki húzza meg az egyes folytatásokat:

  • Egy generátor értékeket ad át; a fogyasztó a next() függvénnyel vagy iterálással húzza le a következőt.

  • Egy korutin a vezérlést adja át; egy eseményhurok ütemezi a folytatást, amikor a várt művelet készen áll.

Ha a generátor-yield kézfogás érthető, akkor a korutin kézfogás ugyanaz az elgondolás – csak egy eseményhurok vezérli egy for ciklus helyett.

Az eseményhurok egy kis diszpécser, amely listát vezet a valamire (egy időzítőre, egy hálózati eseményre, egy másik korutin befejeződésére) várakozó korutinokról. Minden iterációban kiválaszt egy korutint, amelynek a várakozása teljesült, folytatja a következő await pontig, majd feljegyzi, hogy az a korutin most mire vár, és továbblép egy másik készen állóra. Az eredmény az, hogy sok feladat halad előre egyidejűleg egyetlen szálon – minden korutin önként adja át a vezérlést az await pontjainál, és a hurok ezeket a pillanatokat azzal tölti meg, ami éppen készen áll a továbbhaladásra.

A motorháztető alatt az await és a yield ugyanazt a Python futásidejű funkciót használja egy függvény felfüggesztéséhez és folytatásához. A kulcsszavak azért különböznek, mert a köréjük épülő konvenció is különbözik: a yield egy értéket ad vissza a next() függvénnyel húzó fogyasztónak; az await a vezérlést adja át egy eseményhuroknak, amely ütemezi a folytatást, amikor a várt művelet készen áll. Az async / await lényegében újabb szintaxis a korutin mintára – a régebbi könyvtárak közvetlenül a generátor gépezetre építették a korutinokat, a yield from használatával (amelyet az Iterátorok és generátorok mutat be) a felfüggesztés delegálására korutinok között.

2.32.4. A korutinoknak vezérlőre van szükségük

Egy korutin tehetetlen olyan futási környezet nélkül, amely vezérli. Definiálni egyet rendben van; futtatni egyet eseményhurok kell. A MicroPython asyncio modulja biztosítja ezt az eseményhurkot. Az Asyncio szakasz ismerteti, hogyan indítsd el a hurkot, ütemezz rá korutinokat, osztozz az állapoton közöttük zárakkal és eseményekkel, kezeld a megszakítást és az időtúllépéseket, valamint formálj egy valódi alkalmazást az itt bemutatott async / await kulcsszavak köré.