8.2. Koroutine i zadaci¶
Koroutine su jedinica rada od koje je izgrađen asyncio program; zadaci su način na koji aplikacija konkurentno izvodi nekoliko koroutina.
8.2.1. Koroutine¶
Koroutina je funkcija deklarirana s async def
import asyncio
async def heartbeat(interval_ms):
while True:
print("tick")
await asyncio.sleep_ms(interval_ms)
Tijelo izgleda kao obična funkcija, s jednim dodatnim sastojkom: await. Gdje god koroutina mora čekati na nešto – spavanje, mrežno čitanje, postavljanje događaja – awaita izraz koji zna kako suspendirati koroutinu dok ono na što čeka ne bude spremno. Pri svakom await koroutina vraća kontrolu asyncio-u; asyncio je nastavlja od iste točke kad se awaitana operacija dovrši.
Modul asyncio isporučuje dva spavanja:
asyncio.sleep()– argument u sekundama, prihvaća float.asyncio.sleep_ms()– argument u milisekundama, uzima int. MicroPython proširenje; obično ispravan izbor na kameri jer su vremenske postavke u ugrađenom programu oblikovane u milisekundama.
Goli async def sam po sebi ne radi ništa. Pozivanje heartbeat(500) ne izvodi tijelo; vraća objekt koroutine koji asyncio mora rasporediti. Najjednostavniji način za raspoređivanje jedne jest asyncio.run()
asyncio.run(heartbeat(500))
asyncio.run() pokreće petlju događaja, raspoređuje koroutinu koja mu je predana kao ulaznu točku najviše razine, pogoni petlju dok ta koroutina ne vrati vrijednost, zatim ruši petlju. Za jednu koroutinu to je cijeli program. Za nekoliko koroutina aplikacija poseže za zadacima.
8.2.2. Zadaci¶
Zadatak je asyncio-ov omotač oko koroutine koji kaže rasporedi ovo konkurentno s trenutnom i pusti me da nastavim. asyncio.create_task() stvara jedan i vraća objekt Task koji predstavlja raspoređeni rad:
task = asyncio.create_task(heartbeat("fast", 100))
Koroutina je sada na rasporedu petlje; pozivatelj je nije čekao. Vraćeni Task ručka je koju pozivatelj kasnije koristi za interakciju s tim radom koji se izvodi.
Kad aplikacija ima ručku, s njom može učiniti tri stvari:
Pričekati da zadatak završi.
Taskje i sam awaitabilan.result = await tasksuspendira trenutnu koroutinu dok koroutina odtaskne vrati vrijednost, zatim nastavlja s onime što je ta koroutina vratila (ili ponovno podiže ono što je podigla).Otkazati zadatak.
task.cancel()raspoređuje da seasyncio.CancelledErrorpodigne unutar koroutine zadatka pri njezinom sljedećemawait, dajući joj priliku da pokrene kod za čišćenje u blokufinally. Stranica o vremenskim ograničenjima i otkazivanju obrađuje pojedinosti.Identificirati ga kasnije.
asyncio.current_task()vraćaTaskza koroutinu koja se trenutno izvodi. Većina skripti nikad ga ne poziva; pojavljuje se u instrumentaciji i u rukovateljima iznimaka.
Skripta ne mora hvatati ručku svaki put. Jednokratni pozadinski zadaci koje aplikacija pokrene i ostavi da rade mogu odbaciti povratnu vrijednost – petlja ih i dalje raspoređuje:
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())
Dva create_task poziva raspoređuju oba otkucaja srca bez čekanja na bilo koji od njih. Kontrola se odmah vraća u main, koji zatim awaita petsekundno spavanje. Dok spava, dva zadatka otkucaja srca napreduju; petlja kruži kroz onaj zadatak koji je spreman za izvođenje. Nakon pet sekundi main vraća vrijednost, petlja ruši sve zadatke koji su još živi, a asyncio.run() se vraća pozivatelju.
Uhvatite ručku kad god aplikacija doista treba jednu od tri gore navedene operacije. U praksi to znači gotovo uvijek, jer čisto gašenje aplikacije znači otkazivanje pozadinskih zadataka koje je pokrenula – stranica o otkazivanju obrađuje taj obrazac.
8.2.3. Pravilo dvaju redaka¶
Minimalni asyncio program jesu dva retka kojima gornji primjeri završavaju:
async def main():
...
asyncio.run(main())
Sve ostalo – zadaci koje aplikacija stvara, primitivi kojima ih koordinira, tokovi koje otvara – događa se unutar main (i unutar koroutina koje main pokrene). Kad skripta preraste klasičnu kamerinu petlju while True: csi0.snapshot(), odgovor nije pozivati asyncio.run() na više mjesta; odgovor je presaviti novi rad u main kao još zadataka.