8.2. Korutiny a úlohy¶
Korutiny jsou jednotkou práce, ze které je asyncio program postaven; úlohy jsou způsob, jakým aplikace běží několik korutin souběžně.
8.2.1. Korutiny¶
Korutina je funkce deklarovaná pomocí async def
import asyncio
async def heartbeat(interval_ms):
while True:
print("tick")
await asyncio.sleep_ms(interval_ms)
Tělo vypadá jako běžná funkce, jen s jednou přidanou ingrediencí: await. Kdykoli musí korutina na něco čekat — uspání, čtení ze sítě, nastavení události — awaituje výraz, který umí korutinu pozastavit, dokud není to, na co čeká, připraveno. Při každém await korutina předává řízení zpět asyncio; asyncio ji obnoví ze stejného bodu, jakmile se awaitovaná operace dokončí.
Modul asyncio nabízí dvě uspání:
asyncio.sleep()— argument v sekundách, přijímá float.asyncio.sleep_ms()— argument v milisekundách, bere int. Rozšíření MicroPythonu; na kameře obvykle správná volba, protože časovací parametry ve firmwaru mají milisekundový tvar.
Samotné async def neudělá nic. Volání heartbeat(500) tělo nespustí; vrátí objekt korutiny, který musí asyncio naplánovat. Nejjednodušší způsob, jak korutinu naplánovat, je asyncio.run()
asyncio.run(heartbeat(500))
asyncio.run() spustí smyčku událostí, naplánuje předanou korutinu jako vrcholový vstupní bod, řídí smyčku, dokud se tato korutina nevrátí, a poté smyčku ukončí. Pro jednu korutinu je to celý program. Pro několik korutin aplikace sáhne po úlohách.
8.2.2. Úlohy¶
Úloha je obal asyncio kolem korutiny, který říká naplánuj toto souběžně se současnou a nech mě pokračovat. asyncio.create_task() jednu vytvoří a vrátí objekt Task reprezentující naplánovanou práci:
task = asyncio.create_task(heartbeat("fast", 100))
Korutina je nyní v plánu smyčky; volající na ni nečekal. Vrácený objekt Task je rukojeť, kterou volající následně používá k interakci s touto běžící prací.
Jakmile má aplikace rukojeť, může s ní dělat tři věci:
Čekat na dokončení úlohy. Objekt
Taskje sám awaitovatelný.result = await taskpozastaví současnou korutinu, dokud se korutina úlohytasknevrátí, a poté pokračuje s tím, co tato korutina vrátila (nebo znovu vyvolá to, co vyvolala).Zrušit úlohu.
task.cancel()naplánuje vyvoláníasyncio.CancelledErroruvnitř korutiny úlohy při jejím nejbližšímawait, čímž jí dá příležitost spustit úklidový kód v blokufinally. Podrobnosti popisuje stránka o časových limitech a rušení.Identifikovat ji později.
asyncio.current_task()vrací objektTaskpro korutinu, která právě běží. Většina skriptů ji nikdy nevolá; objevuje se v instrumentaci a v obslužných rutinách výjimek.
Skript nemusí rukojeť zachytávat pokaždé. Jednorázové úlohy na pozadí, které aplikace spustí a nechá běžet, mohou návratovou hodnotu zahodit — smyčka je stejně naplánuje:
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())
Dvě volání create_task naplánují oba heartbeaty, aniž by čekala na kterýkoli z nich. Řízení se okamžitě vrací do main, která poté awaituje pětisekundové uspání. Zatímco spí, obě úlohy heartbeat postupují; smyčka cyklicky přepíná na tu úlohu, která je připravena k běhu. Po pěti sekundách se main vrátí, smyčka ukončí všechny úlohy, které jsou stále naživu, a asyncio.run() se vrátí volajícímu.
Rukojeť zachyťte vždy, když aplikace skutečně potřebuje jednu ze tří výše uvedených operací. V praxi to znamená téměř vždy, protože čisté ukončení aplikace znamená zrušit úlohy na pozadí, které spustila — tento vzor popisuje stránka o rušení.
8.2.3. Pravidlo dvou řádků¶
Minimální asyncio program jsou ty dva řádky, kterými výše uvedené příklady končí:
async def main():
...
asyncio.run(main())
Vše ostatní — úlohy, které aplikace vytváří, primitiva, kterými je koordinuje, streamy, které otevírá — se děje uvnitř main (a uvnitř korutin, které main spustí). Když skript přeroste klasickou smyčku kamery while True: csi0.snapshot(), odpovědí není volat asyncio.run() na několika místech; je jí začlenit novou práci do main jako další úlohy.