Skip to content

Concurrencia y asincronia

Python ofrece varias formas de ejecutar trabajo concurrente. Elegir bien depende del tipo de tarea.

Hilos

Los hilos son utiles para tareas de entrada/salida, como llamadas HTTP o lectura de archivos.

python
from concurrent.futures import ThreadPoolExecutor


def download(url: str) -> str:
    return f"contenido de {url}"


urls = ["https://example.com", "https://example.org"]

with ThreadPoolExecutor(max_workers=4) as executor:
    results = list(executor.map(download, urls))

Procesos

Los procesos son mejores para tareas CPU-bound.

python
from concurrent.futures import ProcessPoolExecutor


def square(x: int) -> int:
    return x * x


with ProcessPoolExecutor() as executor:
    results = list(executor.map(square, range(10)))

asyncio

asyncio permite concurrencia cooperativa.

python
import asyncio


async def fetch(name: str) -> str:
    await asyncio.sleep(1)
    return f"result {name}"


async def main():
    results = await asyncio.gather(fetch("a"), fetch("b"))
    print(results)


asyncio.run(main())

Como elegir

  • I/O bloqueante: threads.
  • CPU intensiva: processes.
  • Muchas operaciones async compatibles: asyncio.
  • Trabajo distribuido: Spark, colas o sistemas externos.

Buenas practicas

  • Mide antes de complicar.
  • Evita compartir estado mutable.
  • Controla timeouts.
  • Maneja cancelaciones y errores.
  • No mezcles async y bloqueante sin cuidado.

Errores comunes

  • Pensar que los hilos aceleran cualquier codigo CPU-bound.
  • Llamar funciones bloqueantes dentro de async.
  • No esperar tareas creadas.
  • No limitar concurrencia.

Ejercicio

Simula 10 llamadas lentas con asyncio.sleep, ejecutalas con gather y mide el tiempo total.