Mejorar las tareas asincrónicas con sintaxis mejorada

Tareas asincrónicas con sintaxis mejorada


Explicación


El soporte de Python para la
programación asíncrona se ha desarrollado y mejorado continuamente con el
tiempo, con la adición de generadores  en
Python 2 y la biblioteca asincrónica en Python 3.4, seguido de la 
introducción de las palabras clave async y await en Python 3.5. En
Python 3.11, los desarrolladores ahora pueden usar grupos de tareas, que
ofrecen una sintaxis más conveniente para ejecutar y monitorear tareas
asincrónicas. La  biblioteca asinquina es parte de la biblioteca estándar de Python, sin
embargo, hay varias bibliotecas de terceros como Trio y Curio que proporcionan
capacidades similares y un rendimiento mejorado con características
adicionales. El método tradicional de ejecutar múltiples tareas asincrónicas
con asincio es crear tareas con create_task() y esperarlas con gather(), lo que
puede ser engorroso. Los grupos de tareas, fuertemente inspirados en las
alternativas introducidas por Curio y Trio, proporcionan un método más
organizado para organizar las tareas infantiles.

La

biblioteca asíncrona es una parte fundamental de la biblioteca estándar de
Python para administrar tareas asíncronas. Sin embargo, no es la única opción
para la programación asíncrona en Python. Hay numerosas bibliotecas populares
de terceros disponibles, incluidas Trio y Curio, que ofrecen funcionalidades
similares. Además, paquetes como uvloop, AnyIO y Quattro mejoran el rendimiento
y el conjunto de características de asyncio. Tradicionalmente, el manejo de
varias tareas asincrónicas con asincio implica crear tareas usando create_task()
y luego esperarlas usando gather(). Aunque este método hace el trabajo, puede
ser algo tedioso trabajar con él. Para resolver esto, Curio introdujo grupos de
tareas, y Trio introdujo guarderías, que proporcionan un enfoque alternativo
para organizar las tareas infantiles. Los nuevos grupos de trabajo asincianos
fueron fuertemente influenciados por estas dos bibliotecas. Por ejemplo, cuando
se usa gather(), una sección del código podría verse así:



tasks =
[asyncio.create_task(run_some_task(param)) for param
in params]

await asyncio.gather(*tasks)



En
este ejemplo, realiza un seguimiento manual de todas las tareas de una lista y
las pasa a gather(). Al esperar en gather(), se asegura de que cada tarea se
complete antes de avanzar. El código equivalente usando grupos de tareas es
mucho más sencillo. En lugar de usar gather(), puede usar un administrador de
contexto para definir cuándo se esperarán las tareas:



asincrónico
con asincio. TaskGroup() as tg:

 for  param in params:
tg.create_task(run_some_task(param))



Aquí,
se crea un objeto de grupo de tareas (denominado tg en este ejemplo) y se
utiliza su método .create_task() para crear nuevas tareas. Como ejemplo del
mundo real, consideremos el escenario de descargar varios archivos. Desea
descargar el texto de algunos documentos PEP históricos para comprender cómo
han evolucionado las características asíncronas de Python. Para ser eficiente,
puede usar la biblioteca de terceros aiohttp para descargar los archivos de forma
asincrónica. Primero, importe las bibliotecas necesarias y anote la URL del
repositorio donde se almacena cada texto PEP.



import asyncio

import aiohttpPEP_URL =




"https://raw.githubusercontent.com/python/peps/master/pep-{pep:04d}.txt"



async def main(peps):

async with aiohttp. ClientSession() as  session:await download_peps(session,
peps)async def download_peps(session,
peps)

:tasks = [asyncio.create_task(download_pep(session


,  pep))
for pep in  peps]await asyncio.gather(*tasks)

async def


 download_
pep(session,
 pep):print(f"Descargando PEP

{pep}")url = PEP_URL.format(pep=pep)async with  session.get(url)

as  response:pep_text =

await response.text(

)



title =
pep_text.split("\n")[1].replace("Title:",  "").strip()print(f"Downloaded
PEP  {pep}

: {title}")



Para ejecutar el script, la función main()  se ejecuta de forma asíncrona utilizando el  método asyncio.run() y pasando una lista de números
PEP como argumento. El script descargará los documentos PEP correspondientes e
imprimirá el título de cada PEP una vez que se descargue.



$ python download_peps_gather.py Descargar PEP 492
Descargar PEP 525 Descargar PEP 530 Descargar PEP 3148 Descargar PEP 3156 PEP
3148 descargado: futuros: ejecutar cálculos de forma asíncrona PEP 492
descargado: corutinas con sintaxis asincrónica y espera PEP 530 descargado:
Comprensión asíncrona PEP 3156 descargada: Soporte de E/S asíncrono Reiniciado:
el módulo "asincrónico" PEP 525 descargado: generadores asíncronos



 



Por último, la función main() se
ejecuta de forma asíncrona usando asyncio.run(). La lista de números PEP [492,
525, 530, 3148, 3156] se pasa como argumento a main(), y se inician las
descargas. Se puede observar que todas las descargas se llevan a cabo simultáneamente
ya que todas las tareas indican el inicio de la descarga antes de que
cualquiera de ellas informe de su finalización. Además, es importante señalar
que las tareas se inician en el orden especificado, es decir, en orden numérico
de los PEP. Sin embargo, el orden de finalización de las tareas parece ser
aleatorio. El uso de gather() asegura que todas las tareas se completen antes
de que el código continúe su ejecución. Para actualizar el código para usar un
grupo de tareas en lugar de gather(), se puede hacer una copia de
download_peps_gather.py y nombrar como download_peps_taskgroup.py. La única
modificación requerida sería la función download_peps().



#
download_peps_taskgroup.py



 



# ...



 



Async def download_peps(sesión, PEPS):



asincrónico
con asincio. TaskGroup() como tg:



Para PEP
en PEPS:



tg. create_task(download_pep(sesión, PEP))



 



# ...



 



El
código sigue el patrón establecido para trabajar con tareas asincrónicas en un
grupo de tareas. El grupo de tareas se establece dentro de un administrador de contexto y se crean tareas secundarias para cada tarea
que se ejecutará. El comportamiento del código actualizado permanece sin
cambios en comparación con la versión anterior. El manejo de errores en tareas
asíncronas es un desafío, ya que cualquier tarea puede generar un error en
cualquier momento, e incluso es posible que varias tareas generen errores
simultáneamente. Bibliotecas como Trio y Curio han abordado este problema con un objeto multierror, sin embargo, el
enfoque tiene limitaciones ya que Python carece de soporte incorporado. Con la
introducción de grupos de excepciones en Python 3.11, se ha mejorado el manejo
de errores en los grupos de tareas. Los grupos de excepciones permiten el seguimiento de varios errores
simultáneos, lo que proporciona una mejor compatibilidad con el control de
errores que los métodos anteriores. Para obtener más información sobre los
grupos de tareas y su uso de grupos de excepciones, consulte el tutorial "Python 3.11 Preview: Task and Exception Groups" y, para obtener una comprensión más profunda de los
principios subyacentes, consulte "Razonamiento sobre asincio.
Semáforo" de Guido van Rossum.






Next Post Previous Post