Corutina és un tipus de funció utilitzada en programació asíncrona que permet suspendre i reprendre la seva execució en punts determinats. A diferència de les funcions tradicionals, que s’executen de manera seqüencial fins a finalitzar, les corutines poden cedir el control temporalment i reprendre’l més endavant.

Aquest mecanisme és fonamental en models de concurrència no bloquejant, on múltiples tasques poden avançar de manera cooperativa dins d’un mateix fil d’execució.

Funcionament Una corutina pot suspendre la seva execució en punts específics, habitualment mitjançant instruccions d’espera. Quan una corutina es suspèn, el control es retorna al sistema, que pot continuar executant altres tasques pendents.

Aquest comportament es gestiona sovint mitjançant un bucle d'esdeveniments, que coordina l’execució de múltiples corutines i decideix quan reprendre cadascuna.Documentació oficial de Python: asyncio, https://docs.python.org/3/library/asyncio.html

En llenguatges com Python, les corutines es defineixen amb la paraula clau async i utilitzen l’expressió await per indicar punts de suspensió.

Relació amb altres conceptes Les corutines estan estretament relacionades amb el patró Async/await, que proporciona una sintaxi clara per treballar amb funcions asíncrones. També es vinculen amb estructures com les promeses i els futures, que representen resultats que es completaran en el futur.

Aquest model també es pot considerar una forma de multitasca cooperativa, ja que les corutines decideixen explícitament quan cedir el control.

Implementacions Diversos llenguatges de programació suporten corutines o mecanismes equivalents.

En Python, les corutines formen part del mòdul asyncio, que permet gestionar tasques asíncrones de manera eficient.Matthew Fowler, Python Concurrency with asyncio, Manning, 2022.

En altres llenguatges com Kotlin o C#, existeixen implementacions similars que permeten treballar amb funcions suspensives i models asíncrons.

Exemple En Python, una corutina es pot definir de la següent manera:import asyncio

async def exemple(): await asyncio.sleep(1) print("Executat")

asyncio.run(exemple())En aquest exemple, la funció es suspèn temporalment amb await sense bloquejar l’execució del programa.

Avantatges i limitacions Les corutines permeten gestionar múltiples tasques de manera eficient sense necessitat de crear fils addicionals. A més, faciliten la implementació de models de concurrència no bloquejant.

Tanmateix, poden introduir complexitat en el codi i requereixen una correcta gestió del flux d’execució. Un ús inadequat pot dificultar la depuració i comprensió del programa.

Corutines en programació asíncrona En programació asíncrona, les corutines s'utilitzen per representar operacions que poden quedar suspeses mentre esperen un resultat, com ara una operació d'entrada/sortida, una resposta de xarxa o una consulta a una base de dades. A diferència d'una funció ordinària, una corutina no ha d'executar-se necessàriament de principi a fi sense interrupcions, sinó que pot cedir el control temporalment i reprendre's més endavant.

Aquest comportament és especialment útil en programes concurrents que han de gestionar moltes operacions d'entrada/sortida. Quan una corutina arriba a un punt d'espera, el programa pot permetre que altres corutines avancin, evitant que el fil d'execució quedi bloquejat mentre espera una operació externa. Això fa que les corutines siguin una eina habitual en servidors de xarxa, clients HTTP, aplicacions amb moltes connexions simultànies i altres sistemes orientats a esdeveniments.

Les corutines formen part d'un model de concurrència cooperativa. Això vol dir que el canvi d'una tasca a una altra no es produeix de manera arbitrària per decisió del sistema operatiu, sinó en punts concrets on la corutina cedeix explícitament el control. Aquest model contrasta amb l'ús de fils gestionats pel sistema operatiu, en què el planificador pot interrompre l'execució d'un fil i donar pas a un altre.

Relació amb async/await En molts llenguatges de programació moderns, les corutines s'expressen mitjançant la sintaxi async i await. Una funció declarada com a asíncrona pot produir un objecte que representa una operació pendent, i l'expressió await indica un punt en què l'execució de la corutina pot suspendre's fins que el resultat estigui disponible.

Aquesta sintaxi facilita la lectura del codi asíncron, ja que permet escriure operacions no bloquejants amb una estructura semblant a la del codi seqüencial. Tot i això, l'execució real no és purament lineal: quan una corutina espera una operació, el control pot tornar al mecanisme que coordina les altres tasques pendents.

En Python, el suport modern per a corutines natives amb async i await es va formalitzar amb la PEP 492, que va introduir una sintaxi específica per a corutines i va separar-les conceptualment de les corutines basades en generadors.

Corutines en Python En Python, una corutina es pot definir amb async def. Quan es crida una funció d'aquest tipus, no s'executa immediatament tot el seu cos, sinó que es crea un objecte corutina. Aquest objecte pot ser esperat amb await o programat per executar-se concurrentment dins d'un bucle d'esdeveniments.

La biblioteca asyncio proporciona la infraestructura principal per executar corutines en Python. Segons la documentació oficial, asyncio permet escriure codi concurrent utilitzant la sintaxi async i await, i és la base de diverses biblioteques asíncrones de xarxa i servidors d'alt rendiment.

Dins d'asyncio, les corutines poden executar-se directament amb funcions d'alt nivell, com asyncio.run(), o bé poden ser programades com a tasques. Una tasca és un objecte que encapsula una corutina i la programa perquè s'executi de manera concurrent amb altres tasques dins del mateix bucle d'esdeveniments. La documentació de Python indica que les tasques s'utilitzen per programar corutines concurrentment i que una corutina pot ser embolcallada en una tasca amb funcions com asyncio.create_task().

Diferència amb fils i processos Les corutines no són equivalents als processos ni als fils del sistema operatiu. Un procés té el seu propi espai de memòria, mentre que un fil d'execució comparteix memòria amb altres fils del mateix procés. Les corutines, en canvi, s'executen habitualment dins d'un mateix fil i depenen d'un mecanisme de planificació cooperativa, com ara un bucle d'esdeveniments.

Aquesta diferència té conseqüències importants. Les corutines són especialment útils per a operacions limitades per entrada/sortida, perquè poden suspendre's mentre esperen una resposta externa. En canvi, no proporcionen per si mateixes paral·lelisme real de CPU. Si un programa necessita executar càlculs intensius en paral·lel, sovint és més adequat utilitzar diversos processos o mecanismes específics de paral·lelisme.

També cal tenir en compte que una corutina que executa una operació bloquejant pot impedir que altres corutines avancin. Per aquest motiu, en programes asíncrons és important utilitzar biblioteques compatibles amb l'execució no bloquejant o delegar el treball bloquejant en executors o fils separats.

Avantatges i limitacions Un dels principals avantatges de les corutines és que permeten expressar fluxos de treball asíncrons d'una manera més clara que els models basats exclusivament en callbacks. També poden reduir el cost associat a crear i gestionar molts fils quan el programa ha de mantenir moltes operacions d'entrada/sortida pendents.

Tanmateix, les corutines també tenen limitacions. Requereixen que el codi cooperi amb el model asíncron i cedeixi el control en els punts adequats. A més, si no es gestionen correctament les tasques creades a partir de corutines, poden aparèixer errors difícils de detectar, com excepcions no observades, tasques pendents o recursos que no es tanquen correctament. Per això, en aplicacions reals és habitual combinar corutines amb una gestió explícita de cancel·lacions, temps límit i tancament ordenat de recursos.

Referències

Categoria:Programació