2.32. Coroutine¶
O corutină este o funcție care se poate întrerupe la jumătatea execuției și relua mai târziu din punctul în care a rămas, păstrând intacte toate variabilele sale locale. Reflectă modelul generatorului – un corp de funcție care poate fi suspendat și reluat – cu o singură diferență privind cine controlează fiecare reluare.
Sintaxa Python pentru scrierea unei corutine este perechea de cuvinte-cheie async / await. async marchează funcția drept corutină; await marchează punctele din interiorul ei unde întreruperea este permisă.
2.32.1. Definirea unei corutine¶
O funcție definită cu async def este o funcție corutină. Apelarea ei nu execută corpul; întoarce un obiect corutină care nu a început încă:
async def greet(name):
print("hello,", name)
coro = greet("Alice") # body NOT run yet
Obiectul corutină este întrerupt chiar la începutul funcției. Ceva trebuie să îl controleze pentru ca corpul să ruleze – acel ceva este o buclă de evenimente, o componentă a mediului de execuție. MicroPython oferă una în modulul asyncio. Deocamdată, consideră corutina ca fiind „gata de rulare, în așteptarea unui controlor”.
2.32.2. Întreruperea în interiorul unei corutine¶
În interiorul unei corutine, o expresie await suspendă execuția până când valoarea așteptată este disponibilă:
async def fetch_and_log():
data = await read_sensor()
print("got:", data)
Când corpul ajunge la await read_sensor(), corutina cedează controlul către ceea ce o execută. Când read_sensor() se termină, controlorul reia corutina pe linia următoare, cu rezultatul legat de data.
await este valid doar în interiorul unei corutine. Folosirea lui într-o funcție obișnuită este o eroare de sintaxă.
2.32.3. Relația cu generatoarele¶
Corutinele și generatoarele împărtășesc același mecanism de bază. Diferența constă în cine declanșează fiecare reluare:
Un generator produce valori; consumatorul o extrage pe următoarea cu
next()sau prin iterare.O corutină cedează controlul; o buclă de evenimente programează reluarea când operația așteptată este disponibilă.
Dacă logica de transfer prin generator-yield are sens, transferul prin corutină este aceeași idee – doar că este controlat de o buclă de evenimente în loc de o buclă for.
O buclă de evenimente este un mic dispecer care menține o listă de corutine în așteptarea a ceva (un temporizator, un eveniment de rețea, finalizarea altei corutine). La fiecare iterație alege o corutină a cărei așteptare a fost satisfăcută, o reia până la următorul await, apoi înregistrează ce așteaptă acum acea corutină și trece la alta gata de rulare. Rezultatul este că numeroase sarcini progresează concurent pe un singur fir de execuție – fiecare corutină cedează voluntar controlul la punctele sale await, iar bucla umple acele momente cu orice alte corutine gata să progreseze.
În interior, await și yield folosesc aceeași facilitate a mediului de execuție Python pentru suspendarea și reluarea unei funcții. Cuvintele-cheie diferă deoarece convenția din jurul lor diferă: yield transmite o valoare înapoi unui consumator care o extrage cu next(); await cedează controlul unei bucle de evenimente care programează reluarea când operația așteptată este disponibilă. async / await este în esență o sintaxă mai nouă pentru modelul corutinei – bibliotecile mai vechi construiau corutine direct peste mecanismul generatoarelor, folosind yield from (introdus în Iteratoare și generatoare) pentru a delega suspendarea între corutine.
2.32.4. Corutinele au nevoie de un controlor¶
O corutină este inertă fără un mediu de execuție care să o controleze. Definirea uneia este în regulă; rularea uneia necesită o buclă de evenimente. Modulul asyncio din MicroPython oferă acea buclă de evenimente. Secțiunea Asyncio acoperă modul de pornire a buclei, programarea corutinelor pe ea, partajarea stării între ele cu blocaje și evenimente, gestionarea anulării și a expirărilor de timp, precum și structurarea unei aplicații reale în jurul cuvintelor-cheie async / await introduse aici.