Comprende la concurrencia cooperativa de asyncio: corrutinas perezosas, puntos de suspensión, gather, TaskGroup y las trampas de las llamadas bloqueantes.
Abrir esta lección en Kodokonasyncio implementa una concurrencia cooperativa en un único hilo: un bucle de eventos orquesta corrutinas que ceden el control voluntariamente en cada await. Un punto fundamental, a menudo mal entendido: llamar a una función async def no ejecuta nada. Construye un objeto corrutina inerte cuyo cuerpo solo avanza cuando se espera con await o se planifica como tarea en el bucle.
import asyncio
async def greet() -> str:
return "hello"
coro = greet()
print(type(coro).__name__)
async def main() -> None:
print(await coro)
asyncio.run(main())await marca un punto de suspensión: la corrutina se congela ahí, el bucle recupera el control y hace avanzar otra tarea lista. Entre dos await, tu código se ejecuta sin interrupción - no hacen falta cerrojos para la mayoría de las estructuras. Para lanzar varias corrutinas en paralelo (en el sentido de E/S), envuélvelas en asyncio.gather: tres esperas de un segundo terminan en un segundo en total, no en tres.
import asyncio
import time
async def fetch(name: str, delay: float) -> str:
await asyncio.sleep(delay)
return f"{name} done"
async def main() -> None:
start = time.perf_counter()
results = await asyncio.gather(
fetch("a", 1.0),
fetch("b", 1.0),
fetch("c", 1.0),
)
elapsed = time.perf_counter() - start
print(results)
print(f"total: {elapsed:.1f} s")
asyncio.run(main())Por defecto, gather propaga la primera excepción lanzada pero deja que las demás tareas sigan ejecutándose en segundo plano - rara vez es lo que quieres. Con return_exceptions=True, las excepciones se devuelven como resultados, para clasificarlas después. Desde Python 3.11, prefiere asyncio.TaskGroup: al primer error, todas las tareas hermanas se cancelan limpiamente, y las posibles excepciones múltiples emergen agrupadas en un ExceptionGroup, que se captura con except*.
import asyncio
import time
def blocking_io() -> str:
time.sleep(1)
return "report written"
async def main() -> None:
results = await asyncio.gather(
asyncio.to_thread(blocking_io),
asyncio.sleep(1, result="tick"),
)
print(results)
asyncio.run(main())fetch('a', 1.0) escrita por sí sola, sin await ni create_task?asyncio.sleep(1) lanzados juntos mediante asyncio.gather: ¿qué duración total?time.sleep(2) dentro de una corrutina?