8.15. Buktatók¶
Ugyanazok a minták, amelyek az asyncio-t kellemessé teszik – nincs előzetes kiszorítás, explicit await-ek –, megadják a saját, harapós formáit. Ez az oldal azoknak a katalógusa, amelyek elég gyakran felmerülnek ahhoz, hogy érdemes legyen tudni róluk.
8.15.1. Az await elfelejtése¶
Egy async def függvény meghívása egy korutinobjektumot ad vissza. Nem futtatja le a függvény törzsét. A tényleges végrehajtáshoz a korutint await-elni kell, vagy egy feladatba kell csomagolni:
async def main():
send_request() # bug: returns the coroutine, does nothing
await send_request() # right: run it to completion
asyncio.create_task(send_request()) # right: run it concurrently
A hiba néma – a korutinobjektum létrejön, eldobódik, és soha nem fut le. Az alkalmazás úgy halad tovább, mintha minden működött volna. A MicroPython néha figyelmeztetést naplóz arról, hogy egy korutint soha nem vártak be; néha nem. Ellenőrizd a hiányzó await-eket minden olyan hívási helyen, amely függvényhívásnak tűnik.
8.15.2. Szoros ciklusok await nélkül¶
Egy korutin, amely ciklusban fut, és soha nem await-el, kisajátítja az eseményhurkot. Egyetlen más feladat sem halad előre, amíg a ciklus ki nem lép vagy át nem adja a vezérlést:
async def counter():
n = 0
while True:
n += 1 # bug: starves the loop
A megoldás egy yield a cikluson belül – általában await asyncio.sleep_ms(0) –, hogy más, futásra kész feladatok is esélyt kapjanak a futásra. A számításigényes munka is ebbe a formába tartozik: egy képfeldolgozó ciklusnak, amely iterációnként több száz milliszekundumig fut, iterációnként legalább egyszer át kell adnia a vezérlést, hogy a program többi része ne akadjon el.
8.15.3. A CancelledError elnyelése¶
A megszakítás oldal ezt már részletesen tárgyalta. Azért ismételjük meg itt, mert ez a leggyakoribb oka annak, hogy „az alkalmazásom nem áll le”: egy korutin elkapja az asyncio.CancelledError-t takarítás céljából, és elfelejti újra kiváltani. A feladat tovább fut; a hívó, amely a megszakítást kérte, örökre vár arra, hogy befejeződjön. A takarítás után mindig váltsd ki újra, vagy explicit except helyett használj try/finally blokkot.
8.15.5. Modulszintű await¶
Az await csak egy async def törzsén belül érvényes. Modulszinten való megírása – bármely korutinon kívül – szintaktikai hiba:
# bug: not inside an async def
result = await fetch()
A megoldás az, hogy a munkát egy korutinba helyezed, és a program asyncio.run() belépési pontjából hívod meg.
8.15.6. Több asyncio.run hívás¶
A MicroPythonnak egy eseményhurka van. Az asyncio.run() kétszeri egymás utáni meghívása – egyszer a beállításhoz, egyszer a fő munkához – továbbra is ugyanazt a hurkot használja. Egy futó korutinon belülről való meghívása hiba: a hurok már fut. Mindkét eset leggyakrabban akkor merül fel, amikor egy szkript szervesen növekszik, és a szerző úgy próbálja bővíteni, hogy további run() hívásokat ad hozzá, ahelyett hogy az új munkát beleolvasztaná a meglévő main-be.
8.15.7. Az Event használata megszakításból¶
Az asyncio.Event.set() csak az eseményhurkon belülről hívható biztonságosan. Egy GPIO-megszakításkezelőből való meghívása korrupciós kockázat. Egy feladat megszakításból való felébresztéséhez használd inkább az ThreadSafeFlag-et – az erről szóló oldal ismerteti a formát.
8.15.8. Hosszú szinkron hívások¶
Egy korutin bevárhatja az asyncio saját várakozási primitívjeit; bármi más, amit meghív, szinkronban fut, és blokkolja a hurkot, amíg vissza nem tér. Egy 200 ms-os blokkoló time.sleep(), egy SD-kártya-írás, amelynek 80 ms-ig tart a kiürítése, egy nagy JPEG-tömörítés, egy csi.CSI.snapshot() hívás – ezek mindegyike a teljes időtartamára lefoglalja az eseményhurkot. A megoldás a hívástól függ:
A
time.sleepesetén: cseréld kiawait asyncio.sleep-re vagyawait asyncio.sleep_ms-re.A
csi.CSI.snapshotesetén: használd az aszinkron snapshot-burkolót, amelyet a rögzítési oldal felépít.Hosszú számításhoz (képfeldolgozás, JPEG-kódolás): fogadd el a költséget, vagy bontsd a munkát olyan részekre, amelyek az iterációk között
await-elnek.
Az asyncio nem tudja nem blokkolóvá tenni a szinkron hívásokat. Csak annyit tehet, hogy más korutinokat futtat, miközben valami más vár.