Cómo usar asyncio en Python

La funcionalidad de programación asincrónica de Python, o async para abreviar, le permite escribir programas que hacen más trabajo al no esperar a que terminen las tareas independientes. La asynciobiblioteca incluida con Python le brinda las herramientas para usar async para procesar E / S de disco o red sin hacer que todo lo demás espere.

asyncio proporciona dos tipos de API para tratar con operaciones asincrónicas: de  alto nivel  y de  bajo nivel . Las API de alto nivel son las más útiles en general y se pueden aplicar a la más amplia variedad de aplicaciones. Las API de bajo nivel son potentes, pero también complejas y se utilizan con menos frecuencia.

Nos concentraremos en las API de alto nivel en este artículo. En las secciones siguientes, asyncioanalizaremos las API de alto nivel más utilizadas  y mostraremos cómo se pueden usar para operaciones comunes que involucran tareas asincrónicas. 

Si eres completamente nuevo en async en Python, o podrías usar un repaso sobre cómo funciona, lee mi introducción a Python async antes de sumergirte aquí.

Ejecute corrutinas y tareas en Python

Naturalmente, el uso más común asyncioes ejecutar las partes asincrónicas de su secuencia de comandos de Python. Esto significa aprender a trabajar con rutinas y tareas. 

Los componentes asíncronos de Python, incluidas las corrutinas y las tareas, solo se pueden usar con otros componentes asíncronos, y no con Python sincrónico convencional, por lo que debe  asyncio cerrar la brecha. Para hacer esto, usa la  asyncio.run función:

importar asyncio

async def main ():

print ("Esperando 5 segundos")

para _ en el rango (5):

esperar asyncio.sleep (1)

impresión (".")

print ("Terminado de esperar")

asyncio.run (principal ())

Esto se ejecuta  main(), junto con las corrutinas  main() , y espera a que regrese un resultado.

Como regla general, un programa Python debe tener solo una  .run() declaración, al igual que un programa Python debe tener solo una  main() función. Async, si se usa descuidadamente, puede dificultar la lectura del flujo de control de un programa. Tener un único punto de entrada al código asincrónico de un programa evita que las cosas se pongan complicadas.

Las funciones asincrónicas también se pueden programar como  tareas u objetos que envuelven corrutinas y ayudan a ejecutarlas.

async def my_task ():

hacer algo()

tarea = asyncio.create_task (my_task ())

my_task() luego se ejecuta en el bucle de eventos, con sus resultados almacenados en  task.

Si solo tiene una tarea de la que desea obtener resultados, puede usar  asyncio.wait_for(task) para esperar a que finalice la tarea y luego usar  task.result() para recuperar su resultado. Pero si ha programado una serie de tareas a ejecutar y quiere esperar a que  todos  de ellos para terminar, el uso  asyncio.wait([task1, task2]) de recoger los resultados. (Tenga en cuenta que puede establecer un tiempo de espera para las operaciones si no desea que se ejecuten después de un cierto período de tiempo).

Administrar un bucle de eventos asíncrono en Python

Otro uso común para  asyncio es administrar el bucle de eventos asíncronos  . El bucle de eventos es un objeto que ejecuta funciones asíncronas y devoluciones de llamada; se crea automáticamente cuando lo usas  asyncio.run(). Por lo general, desea usar solo un bucle de eventos asíncronos por programa, nuevamente para mantener las cosas manejables.

Si está escribiendo software más avanzado, como un servidor, necesitará acceso de nivel inferior al bucle de eventos. Con ese fin, puede "levantar el capó" y trabajar directamente con los componentes internos del bucle de eventos. Pero para trabajos sencillos no es necesario.

Leer y escribir datos con transmisiones en Python

Los mejores escenarios para async son las operaciones de red de larga duración, donde la aplicación puede bloquear la espera de que algún otro recurso devuelva un resultado. Con ese fin,  asyncio ofrece flujos, que son mecanismos de alto nivel para realizar E / S de red. Esto incluye actuar como servidor para solicitudes de red.

asyncio usa dos clases,  StreamReader y  StreamWriter, para leer y escribir desde la red a un alto nivel. Si desea leer desde la red, usaría  asyncio.open_connection() para abrir la conexión. Esa función devuelve una tupla de   objetos StreamReader y  StreamWriter, y usaría   métodos .read() y  .write()en cada uno para comunicarse.

Para recibir conexiones de hosts remotos, use  asyncio.start_server(). La asyncio.start_server()función toma como argumento una función de devolución de llamada  client_connected_cb, a la que se llama cada vez que recibe una solicitud. Esa función de devolución de llamada toma instancias de  StreamReader y StreamWriter como argumentos, por lo que puede manejar la lógica de lectura / escritura para el servidor. (Vea aquí un ejemplo de un servidor HTTP simple que usa la   biblioteca asynciocontrolada  aiohttp).

Sincronizar tareas en Python

Las tareas asincrónicas tienden a ejecutarse de forma aislada, pero a veces querrá que se comuniquen entre sí. asyncio proporciona colas y varios otros mecanismos para sincronizar entre tareas:

  • Colas : las  asyncio colas permiten que las funciones asíncronas alineen los objetos de Python para que sean consumidos por otras funciones asíncronas, por ejemplo, para distribuir cargas de trabajo entre diferentes tipos de funciones en función de sus comportamientos.
  • Primitivas de sincronización : bloqueos, eventos, condiciones y semáforos en asynciofuncionamiento como sus contrapartes convencionales de Python. 

Una cosa a tener en cuenta acerca de todos estos métodos es que son  no  seguro para subprocesos. Esto no es un problema para las tareas asíncronas que se ejecutan en el mismo bucle de eventos. Pero si está tratando de compartir información con tareas en un ciclo de eventos, subproceso del sistema operativo o proceso diferente, deberá usar el  threading módulo y sus objetos para hacerlo.

Además, si desea  lanzar  corrutinas a través de los límites del hilo, use la  asyncio.run_coroutine_threadsafe() función y pase el bucle de eventos para usarlo con él como parámetro.

Pausar una corrutina en Python

Otro uso común  asyncioy poco discutido es esperar un período de tiempo arbitrario dentro de una corrutina. No puede usar  time.sleep() para esto, o bloqueará todo el programa. En su lugar, utilice  asyncio.sleep(), que permite que otras corrutinas continúen ejecutándose.

Use async de nivel inferior en Python

Por último, si cree que la aplicación que está creando puede requerir asynciocomponentes de nivel inferior, eche un vistazo antes de comenzar a codificar: es muy probable que alguien ya haya creado una biblioteca de Python con tecnología asíncrona que haga lo que necesita.

Por ejemplo, si necesita consultas DNS asíncronas, consulte la  aiodns biblioteca y, para las sesiones SSH asíncronas, hay  asyncSSH. Busque PyPI por la palabra clave "async" (más otras palabras clave relacionadas con la tarea), o consulte la lista de Awesome Asyncio seleccionada a mano para obtener ideas.