En el mundo de la programación, el concepto de "sin bloqueo" está omnipresente. Los desarrolladores de JavaScript suelen utilizar el término "asincrónico" porque es uno de los puntos fuertes de JavaScript. Sin embargo, para comprender verdaderamente la programación asincrónica, es esencial comprender los conceptos de programación concurrente y paralela.
Cuando varias entidades independientes están trabajando simultáneamente, la programación es concurrente. No significa necesariamente que estas tareas se estén ejecutando exactamente al mismo tiempo. Más bien, significa que las tareas progresan con el tiempo al compartir recursos, como el tiempo de CPU. La principal ventaja de la programación concurrente es su solidez: si un proceso falla, el resto del programa continúa funcionando.
Si un algoritmo puede dividir su trabajo en varias partes, es paralelo. Cuantos más procesadores tenga, más se beneficiará del paralelismo. La programación paralela eficiente optimiza los recursos de las máquinas modernas para lograr un mejor rendimiento.
Ejemplo de concurrencia:
Imagina que estás preparando una comida en la que necesitas asar un poco de carne y hacer una salsa. Se empieza poniendo la carne en la barbacoa. Mientras se asa la carne, se cortan los tomates y otras verduras para la salsa. Luego, comienzas a hervir la salsa mientras revisas de vez en cuando la carne. Aquí, ambas tareas (asar la carne y hacer la salsa) están en progreso, pero estás cambiando tu atención entre ellas. Esto representa simultaneidad.
Ejemplo de paralelismo:
Ahora, digamos que tienes un amigo que te ayuda. Mientras tú te concentras en asar la carne, tu amigo se encarga de hacer la salsa. Ambas tareas se realizan simultáneamente sin necesidad de cambiar la atención entre ellas. Esto representa paralelismo.
La programación asincrónica implica el manejo de operaciones de entrada/salida (E/S) que ocurren fuera de su programa, como la entrada del usuario, la impresión en una terminal, la lectura desde un socket o la escritura en el disco. Las características clave de la E/S asíncrona son:
El tiempo que tarda la operación no depende de la CPU. Más bien, depende de factores como la velocidad del disco, la latencia de la red y otras condiciones externas.
El programa no puede predecir cuándo finalizará la operación.
Para servicios con E/S significativas (como servidores web, bases de datos y scripts de implementación), la optimización de estas operaciones puede mejorar enormemente el rendimiento.
Veamos ejemplos de código de bloqueo y código de no bloqueo.
Considere un programa simple:
import time def task(): time.sleep(2) print("Hello") for _ in range(3): task()
En este programa sincrónico, cada tarea espera a que finalice la anterior, lo que provoca retrasos.
Ahora, veamos una versión asincrónica usando asyncio:
import asyncio async def task(): await asyncio.sleep(2) print("Hello") async def main(): tasks = [task() for _ in range(3)] await asyncio.gather(*tasks) asyncio.run(main())
En este programa asincrónico, las tareas se ejecutan simultáneamente, lo que reduce el tiempo total de ejecución. Exploremos los componentes de la programación asincrónica.
Los bucles de eventos, las corrutinas y los futuros son los elementos esenciales de un programa Python asincrónico.
Event Loop: Administra el cambio de tareas y el flujo de ejecución, realizando un seguimiento de las tareas que se ejecutarán de forma asincrónica.
Corrutinas: Funciones especiales que se pueden pausar y reanudar, lo que permite ejecutar otras tareas durante la espera. Una corrutina especifica en qué parte de la función debe tener lugar el evento de cambio de tarea, devolviendo el control al bucle de eventos. Las corrutinas normalmente las crea el bucle de eventos y se almacenan internamente en una cola de tareas.
Futuros: Marcadores de posición para resultados de corrutinas, que almacenan el resultado o las excepciones. Tan pronto como el bucle de eventos inicia una corrutina, se crea un futuro correspondiente que almacena el resultado de la corrutina, o una excepción si se lanzó una durante la ejecución de la corrutina.
Una vez explicadas las partes cruciales de la programación asincrónica en Python, escribamos algo de código.
Ahora que comprende el patrón de programación asincrónica, escribamos un pequeño script y analicemos la ejecución. Aquí hay un script asincrónico simple:
import asyncio async def task(): await asyncio.sleep(2) print("Hello") async def main(): tasks = [task() for _ in range(3)] await asyncio.gather(*tasks) asyncio.run(main())
En el código anterior, intentamos continuar la ejecución de otras tareas incluso si otra en ejecución está inactiva (bloqueada). Observe la palabra clave async delante de la tarea y las funciones principales.
Esas funciones ahora son corrutinas.
Las funciones de rutina en Python están precedidas por la palabra clave async. La función main() aquí es el coordinador de tareas o nuestro bucle de eventos único, ya que ejecuta todas las tareas utilizando el método async.gather. La función asyncio.gather ejecuta objetos en espera simultáneamente.
Producción:
Hello Hello Hello Program executed in 2.01 seconds.
Cuando cada tarea llega a await asyncio.sleep(2), simplemente pasa a la siguiente tarea y regresa cuando finaliza. Es como decir: "Voy a dormir 2 segundos. Haz otra cosa".
Veamos la versión síncrona para una comparación rápida.
import time def task(): time.sleep(2) print("Hello") for _ in range(3): task()
En el código anterior, vamos a seguir el método de programación tradicional en Python. Notarás que la ejecución del proceso tomará mucho más tiempo.
Producción:
Hello Hello Hello Program executed in 6.01 seconds.
Ahora puedes notar el tiempo de ejecución. Piense en time.sleep() como una tarea de bloqueo y asyncio.sleep() como una tarea larga o sin bloqueo. En la programación asincrónica, el beneficio de esperar algo, como asyncio.sleep(), es que la función circundante puede ceder temporalmente el control a otra función que esté lista para ejecutarse inmediatamente.
Una vez comprendidos algunos ejemplos básicos de programación asincrónica en Python, exploremos las reglas de la programación asincrónica en Python.
Corrutinas: Las corrutinas no se pueden ejecutar directamente. Si intenta ejecutar una función de rutina directamente, devuelve un objeto de rutina. En su lugar, utilice asyncio.run():
import asyncio async def hello(): await asyncio.sleep(1) print('Hello') asyncio.run(hello())
Objetos en espera: Las corrutinas, futuros y tareas son los principales objetos en espera. Las corrutinas de Python están a la espera y se pueden esperar de otras corrutinas.
Palabra clave en espera:await solo se puede usar dentro de funciones asíncronas.
async def hello(): await asyncio.sleep(1) print("Hello")
Compatibilidad: No todos los módulos de Python son compatibles con la programación asincrónica. Por ejemplo, reemplazar await asyncio.sleep() con time.sleep() provocará un error. Puedes consultar la lista de módulos compatibles y mantenidos aquí.
En la siguiente sección, exploraremos un uso común de la programación asincrónica: las solicitudes HTTP.
Echemos un vistazo al siguiente código:
import aiohttp import asyncio async def fetch(session, city): url = f"https://www.prevision-meteo.ch/services/json/{city}" async with session.get(url) as response: data = await response.json() print(f"Temperature at {city}: {data['current_condition']['tmp']} C") async def main(): async with aiohttp.ClientSession() as session: cities = ['paris', 'toulouse', 'marseille'] tasks = [fetch(session, city) for city in cities] await asyncio.gather(*tasks) asyncio.run(main())
En el código anterior, creamos dos funciones asincrónicas: una para recuperar datos de la URL de previsión-meteo y una función principal para ejecutar los procesos en el código Python. El objetivo es enviar solicitudes HTTP GET asincrónicas para recuperar temperaturas e imprimir las respuestas.
En las funciones principal y de recuperación, usamos async with. En la función de recuperación, async with garantiza que la conexión se cierre correctamente. En la función principal, garantiza que ClientSession se cierre después de completar las solicitudes. Estas prácticas son importantes en la codificación asincrónica en Python para administrar los recursos de manera eficiente y evitar fugas.
En la última línea de la función principal, usamos await asyncio.gather(*tasks). En nuestro caso, ejecuta todas las tareas al mismo tiempo, lo que permite que el programa envíe múltiples solicitudes HTTP simultáneamente. El uso de await garantiza que el programa espere a que se completen todas las tareas antes de continuar.
Producción:
Temperature at marseille: 25 C Temperature at toulouse: 24 C Temperature at paris: 18 C Program executed in 5.86 seconds.
Código:
import requests import time def fetch(city): url = f"https://www.prevision-meteo.ch/services/json/{city}" response = requests.get(url) data = response.json() print(f"Temperature at {city}: {data['current_condition']['tmp']} C") def main(): cities = ['paris', 'toulouse', 'marseille'] for city in cities: fetch(city) start_time = time.time() main() print(f"Program executed in {time.time() - start_time:.2f} seconds.")
Producción:
Temperature at Paris: 18 C Temperature at Toulouse: 24 C Temperature at Marseille: 25 C Program executed in 9.01 seconds.
El modelo asincrónico funciona mejor cuando:
Hay una gran cantidad de tareas, lo que garantiza que al menos una tarea siempre pueda progresar.
Las tareas implican E/S significativas, lo que hace que un programa asincrónico pierda mucho tiempo bloqueando cuando podrían estar ejecutándose otras tareas.
Las tareas son en gran medida independientes, lo que minimiza la comunicación entre tareas (y, por lo tanto, que una tarea espere a otra).
En este tutorial, cubrimos:
Los conceptos de programación asincrónica y conceptos relacionados.
Uso efectivo de async/await.
Realizar solicitudes HTTP asíncronas con aiohttp.
Los beneficios de la programación asincrónica.
Gracias por leer. La segunda parte cubrirá la programación asincrónica con Django.
Documentación de Python: rutinas y tareas
Documentación de Python: asyncio - E/S asincrónicas
Documentación de aiohttp
Bibliotecas Aio
Simultaneidad versus paralelismo
Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.
Copyright© 2022 湘ICP备2022001581号-3