Módulo Python asyncio
asyncioEs un módulo de la biblioteca estándar de Python, utilizado para escribir código de operaciones de E/S asíncronas.
asyncio proporciona una manera eficiente de manejar tareas concurrentes, especialmente adecuada para operaciones intensivas en E/S, como solicitudes de red, lectura/escritura de archivos, etc.
Al usar `asyncio`asyncio, puedes manejar múltiples tareas simultáneamente en un solo hilo, sin necesidad de usar múltiples hilos o múltiples procesos.
¿Por qué se necesita asyncio?
En la programación síncrona tradicional, cuando una tarea necesita esperar a que se complete una operación de E/S (como una solicitud de red), el programa se bloquea hasta que la operación finaliza. Esto provoca una baja eficiencia del programa, especialmente cuando se necesitan manejar muchas operaciones de E/S.
asyncioAl introducir el modelo de programación asíncrona, el programa puede continuar ejecutando otras tareas mientras espera operaciones de E/S, mejorando así la concurrencia y la eficiencia del programa.
Imagina que estás administrando un restaurante:
- Modo síncrono (funciones normales):Tienes un solo chef. El cliente A pide un filete, el chef comienza a cocinarlo (esto requiere esperar 5 minutos). Durante esos 5 minutos de cocción, el chef está completamente ocupado y no puede hacer nada más; incluso si el cliente B solo quiere pedir un vaso de agua, tiene que esperar sin hacer nada.
- Modo asíncrono (asyncio):Tienes varios chefs (en realidad sigue siendo uno, pero muy inteligente). Después de que el chef comienza a cocinar el filete del cliente A, se da cuenta de que necesita esperar; inmediatamente marca el filete como en espera y luego se dirige a servir agua al cliente B. Al volver, revisa si el filete está casi listo; si aún no lo está, puede atender el pedido del cliente C. Así, durante el tiempo de espera de E/S (como cocinar el filete, solicitudes de red, lectura/escritura de archivos), el chef (CPU) trabaja eficientemente.
asyncio es la biblioteca estándar que Python utiliza para implementar este inteligente modo de trabajo. Te permite escribir código de concurrencia de un solo hilo, especialmente adecuado para escenarios intensivos en E/S como rastreadores web, servidores web, microservicios, etc.
Su núcleo son el bucle de eventos, las corrutinas y las tareas.
Conceptos centrales de asyncio
1. Corrutina (Coroutine)
Una corrutina esasynciouno de los conceptos centrales de `asyncio`. Es una función especial que puede pausarse durante su ejecución y reanudarse más tarde. Las corrutinas se definen medianteasync defla palabra clave `async def` y, medianteawaitla palabra clave `await` se pausa la ejecución, esperando que se complete la operación asíncrona.
Ejemplo
async def say_hello():
print("Hello")
await asyncio.sleep(1)
print("World")
2. Bucle de eventos (Event Loop)
El bucle de eventos esasyncioel componente central de `asyncio`, responsable de programar y ejecutar corrutinas. Comprueba continuamente si hay tareas que ejecutar y, cuando se completan, llama a las funciones de devolución de llamada correspondientes.
Ejemplo
await say_hello()
asyncio.run(main())
3. Tarea (Task)
Una tarea es una envoltura de una corrutina, que representa una corrutina en ejecución o por ejecutarse. Puedesasyncio.create_task()usar la función `create_task()` para crear tareas y agregarlas al bucle de eventos.
Ejemplo
task = asyncio.create_task(say_hello())
await task
4. Future
Future`Future` es un objeto que representa el resultado de una operación asíncrona. Generalmente se usa en API de bajo nivel para representar una operación aún no completada. Puedesawaitusar la palabra clave `await` para esperarFuturea que `Future` se complete.
Ejemplo
future = asyncio.Future()
await future
Uso básico y ejemplos de código
Supongamos que necesitamos obtener el contenido de tres URLs diferentes. Con el modo síncrono se ejecutarán secuencialmente, y el tiempo total será la suma de los tiempos de las tres solicitudes. Usando
`asyncio`, podemos enviar las tres solicitudes simultáneamente, y el tiempo total se acercará al de la solicitud más lenta.asyncioVersión síncrona (como comparación)
Versión síncrona (como comparación)
Ejemplo
import requests
def fetch_url(url):
`"Iniciando obtención: {url}"`
print(f`# Simula 2 segundos de latencia de red`)
time.sleep(2) `"Obtención completada: {url}"`
print(f`"Datos de {url}"`)
return f`# Debe esperar a que el anterior termine para comenzar el siguiente`
def main_sync():
urls = ['https://example.com/1', 'https://example.com/2', 'https://example.com/3']
results = []
start = time.time()
for url in urls:
result = fetch_url(url) `"Tiempo total de la versión síncrona: {end - start:.2f} segundos"`
results.append(result)
end = time.time()
print(f`"Resultados: {results}"`)
print(fResultado esperado de ejecución)
if __name__ == "__main__":
main_sync()
En total tomó aproximadamente 6 segundos.:
开始获取: https://example.com/1 完成获取: https://example.com/1 开始获取: https://example.com/2 完成获取: https://example.com/2 开始获取: https://example.com/3 完成获取: https://example.com/3 同步版本总耗时: 6.00 秒 结果: [‘来自 https://example.com/1 的数据‘, ‘来自 https://example.com/2 的数据‘, ‘来自 https://example.com/2 的数据‘]
Versión asíncrona (usando asyncio)
Versión asíncrona (usando asyncio)
la biblioteca `aiohttp` en lugar deaiohttp`requests` para realizar solicitudes HTTP asíncronas. Primero instálala:requestsEjemplopip install aiohttp。
Ejemplo
import aiohttp
import time
async def fetch_url_async(session, url):
`"Iniciando obtención asíncrona: {url}"`
print(f`# Nota: aquí usamos el método asíncrono get de aiohttp, y esperamos con await`)
`# Simula que procesar la respuesta también requiere tiempo`
async with session.get(url) as response:
`# Usa asyncio.sleep para simular la espera de E/S; no bloquea el hilo`
await asyncio.sleep(2) `"Obtención asíncrona completada: {url}"`
text = await response.text()
print(f`"Datos de {url} (longitud: {len(text)})"`)
return f`# Crea una sesión HTTP asíncrona`
async def main_async():
urls = ['https://httpbin.org/get', 'https://httpbin.org/delay/1', 'https://httpbin.org/headers']
async with aiohttp.ClientSession() as session: `# Crea una tarea (Task) para cada URL`
`# create_task agregará la corrutina al bucle de eventos y comenzará a programarla de inmediato`
tasks = []
for url in urls:
`"Todas las tareas han sido creadas, comenzando la ejecución concurrente..."`
task = asyncio.create_task(fetch_url_async(session, url))
tasks.append(task)
print(`# Usa asyncio.gather para ejecutar todas las tareas concurrentemente y esperar a que todas terminen`)
`# gather devuelve una lista de resultados, cuyo orden coincide con el orden de las tareas pasadas`
`# asyncio.run() es una forma conveniente de iniciar el bucle de eventos y ejecutar la corrutina principal`
results = await asyncio.gather(*tasks)
return results
if __name__ == "__main__":
start = time.time()
`"Tiempo total de la versión asíncrona: {end - start:.2f} segundos"`
final_results = asyncio.run(main_async())
end = time.time()
print(f"\nResultado esperado de ejecución)
for res in final_results:
print(res)
Análisis del código:
所有任务已创建,开始并发执行... 开始异步获取: https://httpbin.org/get 开始异步获取: https://httpbin.org/delay/1 开始异步获取: https://httpbin.org/headers (大约 2 秒后,所有请求几乎同时完成) 完成异步获取: https://httpbin.org/headers 完成异步获取: https://httpbin.org/get 完成异步获取: https://httpbin.org/delay/1 异步版本总耗时: 2.10 秒 # 注意!总耗时远小于 6 秒 来自 https://httpbin.org/get 的数据 (长度: 274) 来自 https://httpbin.org/delay/1 的数据 (长度: 392) 来自 https://httpbin.org/headers 的数据 (长度: 177)
`async def fetch_url()`: define la función de corrutina:
async def`await asyncio.sleep(2)`: enfetch_url_asyncymain_async。await`fetch_url`, usamos `await asyncio.sleep(2)`fetch_url_async, lo que le dice al bucle de eventos: "Esta solicitud de red necesita tiempo, ve a ejecutar otras tareas listas".await session.get()yawait response.text()`asyncio.create_task()`: envuelveasyncio.create_task()la corrutina `fetch_url` en unfetch_url_async`Task`, lo que permite que el bucle de eventos la programe, logrando concurrencia.Task`asyncio.gather()`: una función muy práctica que ejecuta concurrentemente todas las corrutinas/tareas pasadas, espera a que todas terminen y, finalmente, recopila todos los resultados.asyncio.gather(*tasks)`asyncio.run()`: el método recomendado en Python 3.7+, que se encarga de crear el bucle de eventos, ejecutar la corrutina y cerrar el bucle.asyncio.run(main_async())Explicación de funciones clave y parámetros
Explicación de funciones clave y parámetros
algunas de las funciones avanzadas más utilizadas en `asyncio`:asyncioFunción
| Propósito principal | Descripción de parámetros comunes | Ejecuta una corrutina de nivel superior y gestiona el ciclo de vida del bucle de eventos. Es el punto de entrada principal del programa. |
|---|---|---|
asyncio.run(coro, *, debug=False) |
`coro`: el objeto de corrutina que se va a ejecutar. | coro`debug`: si se establece endebug`True`, se puede habilitar el modo de depuración del bucle de eventos.TrueEnvuelve una corrutina en un |
asyncio.create_task(coro, *, name=None) |
objeto `Task` y lo coloca en el bucle de eventos para que sea programado. Esta es la principal forma de lograr concurrencia.Task`coro`: el objeto de corrutina que se va a envolver. | coro`name`: (Python 3.8+) especifica un nombre para la tarea, lo que facilita la depuración.nameEjecuta concurrentemente |
asyncio.gather(*aws, return_exceptions=False) |
múltiples tareas asíncronas (acepta corrutinas, tareas, etc.), espera a que todas se completen y devuelve una lista de resultados.aws`*aws`: parámetro variable, se pasan múltiples objetos asíncronos. |
*aws`return_exceptions`: por defecto esreturn_exceptions, cualquier excepción lanzada por una tarea se propagará inmediatamente aFalsequien llama a `gather`. Si se establece engather`True`, las excepciones se devolverán como parte de los resultados.TrueDe forma asíncrona |
asyncio.sleep(delay, result=None) |
duerme la cantidad de segundos especificada. Esta es la diferencia clave con`time.sleep()` (bloqueante).time.sleepLa diferencia clave de (bloqueo). |
delayNúmero de segundos para dormir.resultValor que se devuelve después de que termina el sueño. |
asyncio.wait(aws, *, timeout=None, return_when=ALL_COMPLETED) |
Ejecutar tareas concurrentemente y esperar a que se cumpla la condición especificada. Devuelve dos conjuntos(done, pending), que son las tareas completadas y las no completadas, respectivamente. |
awsConjunto de objetos asíncronos.timeoutTiempo de espera (en segundos).return_whenDetermina cuándo regresar, opcional:FIRST_COMPLETED(el primero en completarse),FIRST_EXCEPTION(la primera excepción),ALL_COMPLETED(todos completados, por defecto). |
asyncio.to_thread(func, /, *args, **kwargs) |
(Python 3.9+) Toma una función síncrona ordinaria y potencialmente bloqueante, la ejecuta en un hilo separado y devuelve unaawaitcoroutine que se puede esperar. Se utiliza para manejar trabajo intensivo de CPU o E/S bloqueante. |
funcLa función síncrona que se ejecutará en el hilo.*args, **kwargsLos argumentos que se pasan a la función. |
Comprensión visual: flujo de programación de tareas asíncronas

Explicación gráfica:awaitPor ejemplo, cuando espera una respuesta de la red, queda suspendida y el bucle de eventos inmediatamente busca en la cola la siguiente tarea que pueda ejecutarse (lista) para ejecutarla. Cuando la operación de E/S de la tarea suspendida se completa, el bucle de eventos recibe la notificación, cambia el estado de la tarea de nuevo a lista y continúa ejecutándola en algún momento futuro. De esta manera, durante la espera de E/S, la CPU se utiliza plenamente para ejecutar otras tareas, logrando concurrencia dentro de un solo hilo.
Uso básico de asyncio
1. Ejecutar una corrutina
Para ejecutar una corutina, puedes usar laasyncio.run()función. Esta crea un bucle de eventos y ejecuta la corutina especificada.
Ejemplo
async def main():
print("Start")
await asyncio.sleep(1)
print("End")
asyncio.run(main())
2. Ejecutar múltiples tareas concurrentemente
Puedes usar laasyncio.gather()función para ejecutar varias corutinas concurrentemente y esperar a que todas se completen.
Ejemplo
async def task1():
print("Task 1 started")
await asyncio.sleep(1)
print("Task 1 finished")
async def task2():
print("Task 2 started")
await asyncio.sleep(2)
print("Task 2 finished")
async def main():
await asyncio.gather(task1(), task2())
asyncio.run(main())
3. Control de tiempo de espera
Puedes usar laasyncio.wait_for()función para establecer un tiempo de espera para la corutina. Si la corutina no se completa en el tiempo especificado, se lanzaráasyncio.TimeoutErroruna excepción.
Ejemplo
async def long_task():
await asyncio.sleep(10)
print("Task finished")
async def main():
try:
await asyncio.wait_for(long_task(), timeout=5)
except asyncio.TimeoutError:
print("Task timed out")
asyncio.run(main())
Escenarios de aplicación de asyncio
asyncioEs especialmente adecuado para los siguientes escenarios:
- Solicitudes de red: como solicitudes HTTP, comunicación WebSocket, etc.
- E/S de archivos: como lectura y escritura de archivos asíncronas.
- Operaciones con bases de datos: como acceso asíncrono a bases de datos.
- Procesamiento de datos en tiempo real: como procesamiento de colas de mensajes en tiempo real.
Clases, métodos y funciones comunes
1. Funciones principales
| Método/Función | Descripción | Ejemplo |
|---|---|---|
asyncio.run(coro) |
Ejecutar la función principal asíncrona (Python 3.7+) | asyncio.run(main()) |
asyncio.create_task(coro) |
Crear tarea y añadirla al bucle de eventos | task = asyncio.create_task(fetch_data()) |
asyncio.gather(*coros) |
Ejecutar varias corutinas concurrentemente | await asyncio.gather(task1, task2) |
asyncio.sleep(delay) |
Espera asíncrona (no bloqueante) | await asyncio.sleep(1) |
asyncio.wait(coros) |
Controlar el modo de finalización de las tareas | done, pending = await asyncio.wait([task1, task2]) |
2. Bucle de eventos (Event Loop)
| Método | Descripción | Ejemplo |
|---|---|---|
loop.run_until_complete(future) |
Ejecutar hasta que la tarea se complete | loop.run_until_complete(main()) |
loop.run_forever() |
Ejecutar el bucle de eventos para siempre | loop.run_forever() |
loop.stop() |
Detener el bucle de eventos | loop.stop() |
loop.close() |
Cerrar el bucle de eventos | loop.close() |
loop.call_soon(callback) |
Programar la ejecución inmediata de una función de devolución de llamada | loop.call_soon(print, "Hello") |
loop.call_later(delay, callback) |
Ejecución retrasada de la función de devolución de llamada | loop.call_later(5, callback) |
3. Corutinas (Coroutine) y tareas (Task)
| Método/Decorador | Descripción | Ejemplo |
|---|---|---|
@asyncio.coroutine |
Decorador de corutinas (versión anterior, Python 3.4-3.7) | @asyncio.coroutine def old_coro(): |
async def |
Definir corutina (Python 3.5+) | async def fetch(): |
task.cancel() |
Cancelar tarea | task.cancel() |
task.done() |
Verificar si la tarea está completada | if task.done(): |
task.result() |
Obtener el resultado de la tarea (requiere que la tarea esté completada) | data = task.result() |
4. Primitivas de sincronización (similares athreading)
| Clase | Descripción | Ejemplo |
|---|---|---|
asyncio.Lock() |
Candado de exclusión mutua asíncrono | lock = asyncio.Lock() async with lock: |
asyncio.Event() |
Notificación de eventos | event = asyncio.Event() await event.wait() |
asyncio.Queue() |
Cola asíncrona | queue = asyncio.Queue() await queue.put(item) |
asyncio.Semaphore() |
Semáforo | sem = asyncio.Semaphore(5) async with sem: |
5. Redes y subprocesos
| Método/Clase | Descripción | Ejemplo |
|---|---|---|
asyncio.open_connection() |
Establecer conexión TCP | reader, writer = await asyncio.open_connection('host', 80) |
asyncio.start_server() |
Crear servidor TCP | server = await asyncio.start_server(handle, '0.0.0.0', 8888) |
asyncio.create_subprocess_exec() |
Crear subproceso | proc = await asyncio.create_subprocess_exec('ls') |
6. Herramientas útiles
| Método | Descripción | Ejemplo |
|---|---|---|
asyncio.current_task() |
Obtener la tarea actual | task = asyncio.current_task() |
asyncio.all_tasks() |
Obtener todas las tareas | tasks = asyncio.all_tasks() |
asyncio.shield(coro) |
Proteger la tarea de la cancelación | await asyncio.shield(critical_task) |
asyncio.wait_for(coro, timeout) |
Espera con tiempo de espera | try: await asyncio.wait_for(task, 5) |
Ejemplo
1. Ejemplo básico de corutina
Ejemplo
async def hello():
print("Hello")
await asyncio.sleep(1)
print("World")
asyncio.run(hello()) # Python 3.7+
2. Ejecución concurrente de tareas
Ejemplo
print(f"Fetching {url}")
await asyncio.sleep(2)
return f"Data from {url}"
async def main():
results = await asyncio.gather(
fetch("url1.com"),
fetch("url2.com")
)
print(results)
asyncio.run(main())
3. Usar una cola asíncrona
Ejemplo
for i in range(5):
await queue.put(i)
await asyncio.sleep(0.1)
async def consumer(queue):
while True:
item = await queue.get()
print(f"Consumed {item}")
queue.task_done()
async def main():
queue = asyncio.Queue()
await asyncio.gather(
producer(queue),
consumer(queue)
)
Notas importantes
-
Versión de Python: algunas funciones requieren Python 3.7+ (por ejemplo,
asyncio.run())。 -
Operaciones bloqueantes: evita usar código síncrono bloqueante en las corutinas (por ejemplo,
time.sleep())。 -
Depuración: configurar la
PYTHONASYNCIODEBUG=1variable de entorno habilita el modo de depuración. -
Cancelación de tareas: una tarea cancelada lanza
CancelledError, por lo que debe manejarse adecuadamente.