8.2. Korutinok és feladatok¶
A korutinok az a munkaegység, amelyből egy asyncio program felépül; a feladatok pedig azt jelentik, ahogyan egy alkalmazás több korutint futtat párhuzamosan.
8.2.1. Korutinok¶
A korutin egy async def-fel deklarált függvény:
import asyncio
async def heartbeat(interval_ms):
while True:
print("tick")
await asyncio.sleep_ms(interval_ms)
A törzs úgy néz ki, mint egy szokványos függvényé, egyetlen extra összetevővel: az await-tel. Ahol a korutinnak várnia kell valamire — egy alvásra, egy hálózati olvasásra, egy esemény beállítására —, ott await-el egy olyan kifejezést, amely tudja, hogyan függessze fel a korutint, amíg az, amire vár, készen nem áll. Minden await-nél a korutin visszaadja a vezérlést az asyncio-nak; az asyncio ugyanarról a pontról folytatja, amint a várt művelet befejeződött.
Az asyncio modul kétféle alvást kínál:
asyncio.sleep()— az argumentum másodpercben, float-ot fogad el.asyncio.sleep_ms()— az argumentum ezredmásodpercben, int-et vár. Ez egy MicroPython kiterjesztés; általában ez a helyes választás a kamerán, mivel a firmware időzítési beállításai ezredmásodperc-alapúak.
Egy puszta async def önmagában semmit nem csinál. A heartbeat(500) hívása nem hajtja végre a törzset; egy korutin objektumot ad vissza, amelyet az asyncio-nak kell ütemeznie. Egy ilyet a legegyszerűbben az asyncio.run() segítségével ütemezhetsz:
asyncio.run(heartbeat(500))
Az asyncio.run() elindítja az eseményhurkot, ütemezi a neki átadott korutint felső szintű belépési pontként, mozgatja a hurkot, amíg az a korutin vissza nem tér, majd lebontja a hurkot. Egyetlen korutin esetén ez az egész program. Több korutin esetén az alkalmazás a feladatokhoz nyúl.
8.2.2. Feladatok¶
A feladat az asyncio burkolója egy korutin köré, amely azt mondja: ütemezd ezt az aktuálissal párhuzamosan, és hadd menjek tovább. Az asyncio.create_task() létrehoz egyet, és visszaad egy Task objektumot, amely az ütemezett munkát képviseli:
task = asyncio.create_task(heartbeat("fast", 100))
A korutin most már a hurok ütemezésén van; a hívó nem várt rá. A visszaadott Task az a fogantyú, amelyet a hívó utána a futó munkával való interakcióhoz használ.
Miután az alkalmazásnak megvan a fogantyúja, három dolgot tehet vele:
Megvárhatja a feladat befejeződését. Egy
Taskmaga is await-elhető. Aresult = await taskfelfüggeszti az aktuális korutint, amíg ataskkorutinja vissza nem tér, majd azzal folytatja, amit az a korutin visszaadott (vagy újra dobja, amit az dobott).Megszakíthatja a feladatot. A
task.cancel()ütemezi, hogy azasyncio.CancelledErrora feladat korutinján belül dobódjon a következőawait-jénél, lehetőséget adva neki, hogy egyfinallyblokkban lefuttassa a takarító kódot. A részleteket a időtúllépések és megszakítás oldal tárgyalja.Később azonosíthatja. Az
asyncio.current_task()visszaadja az éppen futó korutinTaskobjektumát. A legtöbb szkript soha nem hívja meg; eszközökben (instrumentációban) és kivételkezelőkben jelenik meg.
A szkriptnek nem kell minden alkalommal elkapnia a fogantyút. Az eldobható háttérfeladatok, amelyeket az alkalmazás elindít és futni hagy, elhagyhatják a visszatérési értéket — a hurok így is ütemezi őket:
import asyncio
async def heartbeat(name, interval_ms):
while True:
print(name)
await asyncio.sleep_ms(interval_ms)
async def main():
asyncio.create_task(heartbeat("fast", 100))
asyncio.create_task(heartbeat("slow", 500))
await asyncio.sleep(5)
asyncio.run(main())
A két create_task hívás mindkét heartbeatet ütemezi anélkül, hogy bármelyikre is várna. A vezérlés azonnal visszatér a main-be, amely ezután await-el egy ötmásodperces alvást. Amíg alszik, a két heartbeat feladat előrehalad; a hurok körbejár azon, amelyik feladat éppen készen áll a futásra. Öt másodperc után a main visszatér, a hurok lebontja a még élő feladatokat, és az asyncio.run() visszatér a hívóhoz.
Akkor kapd el a fogantyút, amikor az alkalmazásnak ténylegesen szüksége van a fenti három művelet valamelyikére. A gyakorlatban ez szinte mindig azt jelenti, mert egy alkalmazás tiszta leállítása az általa indított háttérfeladatok megszakítását jelenti — a megszakítási oldal tárgyalja a mintát.
8.2.3. A kétsoros szabály¶
A minimális asyncio program az a két sor, amellyel a fenti példák végződnek:
async def main():
...
asyncio.run(main())
Minden más — a feladatok, amelyeket az alkalmazás létrehoz, a primitívek, amelyekkel összehangolja őket, a streamek, amelyeket megnyit — a main-en belül történik (és a main által indított korutinokon belül). Amikor egy szkript kinövi a kamera klasszikus while True: csi0.snapshot() ciklusát, a válasz nem az, hogy több helyen hívjuk meg az asyncio.run() függvényt; hanem hogy az új munkát további feladatokként hajtsuk be a main-be.