TaskGroup eager_start: controla el inicio de tareas

Publicado el: 23/09/2026
Tempo de leitura: 7 minutos
Desarrollador trabajando con tareas asíncronas y TaskGroup eager_start en Python

La opción eager_start de asyncio.TaskGroup.create_task() permite controlar con más precisión cuándo comienza a ejecutarse una corrutina. En programas asíncronos, los detalles del planificador pueden cambiar el orden de los efectos secundarios, el momento en que aparece una excepción y el coste de operaciones muy breves. Comprender el inicio ansioso ayuda a usar concurrencia estructurada sin depender de comportamientos accidentales del event loop.

Este artículo explica qué significa ejecutar una tarea de forma ansiosa, cómo se relaciona con TaskGroup, cuándo puede mejorar el rendimiento, qué cambios semánticos debes vigilar y cómo probarlo con seguridad. La idea no es añadir un argumento nuevo sin más, sino entender sus consecuencias.

Por qué TaskGroup es importante

asyncio.TaskGroup trata varias tareas relacionadas como una unidad supervisada. Las tareas se crean dentro de un gestor de contexto asíncrono y, al salir del bloque, el grupo espera a que todas terminen. Si una falla, las tareas hermanas se cancelan de forma coordinada y los errores se agrupan. Así se reducen tareas huérfanas y la propiedad de la concurrencia queda visible.

Para ampliar conceptos relacionados, consulta asyncio.Barrier en Python, asyncio.eager_task_factory en Python, asyncio.Queue.shutdown en Python y sys.monitoring en Python. Estos artículos cubren sincronización, ejecución inmediata, cierre ordenado y observabilidad.

Qué cambia con eager_start

En el flujo habitual, crear una tarea programa su corrutina para que el event loop la ejecute más adelante. Con inicio ansioso, la corrutina puede empezar durante la propia creación de la tarea y avanzar hasta su primer punto de suspensión. Si termina sin hacer await, puede completarse sin una vuelta adicional del bucle.

Esto puede reducir overhead en corrutinas pequeñas, como consultas a caché local, validaciones en memoria, cálculos memoizados o adaptadores que suelen devolver un resultado antes de hacer I/O. Sin embargo, también modifica el orden observable: parte de la corrutina puede ejecutarse exactamente en el punto donde se crea la tarea.

Ejemplo básico

import asyncio

async def leer_cache(clave):
    print(f"inicio: {clave}")
    if clave == "usuario:1":
        return {"nombre": "Ana"}
    await asyncio.sleep(0.1)
    return None

async def main():
    async with asyncio.TaskGroup() as grupo:
        tarea = grupo.create_task(
            leer_cache("usuario:1"),
            eager_start=True,
        )

    print(tarea.result())

asyncio.run(main())

Cuando la versión de Python y la implementación utilizada aceptan el argumento, la ruta de acierto de caché puede terminar inmediatamente porque no alcanza ningún await. En una pérdida de caché, la corrutina avanza hasta la suspensión y continúa después mediante el event loop.

Compatibilidad de versiones

Las novedades recientes de asyncio exigen definir con claridad la versión mínima de Python. Verifica la documentación de la versión usada en desarrollo, CI, contenedores y producción. Una biblioteca que soporta varias versiones puede necesitar una ruta compatible que no pase el nuevo argumento.

No conviene capturar cualquier TypeError de forma indiscriminada, porque ese error también puede señalar otro defecto. Es mejor declarar la versión mínima, comprobar una capacidad conocida o centralizar la compatibilidad en una función pequeña y bien probada.

Buenos casos de uso

Los mejores candidatos son corrutinas que con frecuencia terminan de manera síncrona: lecturas de caché, configuración ya cargada, normalización simple, deduplicación, comprobaciones de permisos sobre estado local y resultados memoizados. En estos casos puede ahorrarse una pequeña ronda del planificador.

El beneficio debe medirse. Compara percentiles de latencia, throughput y CPU con cargas representativas. El artículo sobre perf_counter_ns en Python explica cómo medir operaciones pequeñas con repeticiones y evitar conclusiones basadas en una sola muestra.

Cambios en el orden de ejecución

Imagina un bucle que registra un mensaje antes y después de crear cada tarea. Con planificación convencional, todos los mensajes del creador pueden aparecer antes de los mensajes internos. Con inicio ansioso, la corrutina puede imprimir o modificar estado entre ambos mensajes.

async def trabajo(numero):
    print("corrutina", numero)
    await asyncio.sleep(0)

async def ejecutar():
    async with asyncio.TaskGroup() as grupo:
        for numero in range(3):
            print("antes", numero)
            grupo.create_task(trabajo(numero), eager_start=True)
            print("después", numero)

No bases la corrección del programa en un orden casual de logs. Cuando el orden es un requisito, exprésalo con colas, eventos, barreras, locks o dependencias explícitas. El inicio ansioso hace visibles supuestos que ya eran frágiles.

Excepciones

Una corrutina puede lanzar una excepción antes del primer await. Con inicio ansioso, el fallo puede aparecer muy pronto en el flujo de creación. TaskGroup sigue coordinando errores y cancelaciones, pero el momento observable puede diferir de una ejecución siempre diferida.

Prueba excepciones antes y después de la suspensión. Comprueba que las tareas hermanas se cancelan, los recursos se cierran y el grupo de excepciones contiene los fallos esperados. No te limites a probar el orden de mensajes.

Cancelación

La cancelación sigue siendo cooperativa. Las corrutinas deben permitir que asyncio.CancelledError se propague después de la limpieza necesaria. Usa bloques finally para liberar recursos y mantén la limpieza acotada. Ignorar la cancelación puede retrasar todo el grupo.

También debes probar la cancelación de una tarea que normalmente termina de inmediato. Un cambio de caché o configuración puede introducir una suspensión en producción. Ambas rutas deben mantener las mismas invariantes del dominio.

No bloquees el event loop

El inicio ansioso no convierte el trabajo pesado de CPU en algo adecuado para el hilo del event loop. Un parser grande, una compresión prolongada, un cálculo criptográfico o un bucle costoso ejecutado antes del primer await bloquea de inmediato todas las demás operaciones asíncronas.

Mueve el trabajo CPU-bound a un ejecutor apropiado. Revisa InterpreterPoolExecutor en Python y kill_workers en ProcessPoolExecutor para conocer estrategias de paralelismo y cierre de workers.

Estrategia de adopción

Empieza en un punto aislado y medible. Documenta por qué se eligió el inicio ansioso. Evalúa aciertos y pérdidas de caché, errores, cancelaciones y telemetría. Amplía su uso solo cuando el beneficio sea estable y el cambio de orden esté bien entendido.

Una corrutina adecuada es corta, no bloqueante, predecible antes del primer await y sin efectos secundarios globales inesperados. Una mala candidata realiza cómputo pesado, modifica estado compartido, invoca callbacks del usuario durante la creación o depende de un orden específico del planificador.

Pruebas recomendadas

Cubre finalización síncrona, suspensión por I/O, fallo antes de suspender, fallo después de suspender, cancelación del padre, cancelación de tareas hermanas y limpieza. Prueba resultados del dominio: valores devueltos, recursos liberados, estado consistente y tipos de excepción correctos.

import asyncio

async def valor_inmediato():
    return 42

async def prueba():
    async with asyncio.TaskGroup() as grupo:
        tarea = grupo.create_task(
            valor_inmediato(),
            eager_start=True,
        )
    assert tarea.result() == 42

Observabilidad

Las tareas muy breves pueden comenzar y terminar entre puntos normales de monitorización. Incluye identificadores de operación, nombre de tarea y request ID en logs. Confirma que tracing y profiling siguen mostrando tareas rápidas. Mide promedio y percentiles altos, porque una mejora del camino rápido puede ocultar un bloqueo en otra ruta.

Diseño de API

Si expones una función auxiliar que crea tareas, decide si el llamador debe controlar el inicio ansioso. Un argumento explícito documenta la elección semántica. Ocultarlo como valor global puede sorprender a quien no espera efectos secundarios durante la creación. Los valores predeterminados deberían priorizar previsibilidad.

Buenas prácticas

Usa eager_start de forma intencional, confirma la versión compatible, mantén pequeño el trabajo anterior al primer await, conserva la cancelación, evita depender de orden implícito y mide cargas reales. Una optimización solo es útil si el sistema sigue siendo comprensible.

Conclusión

TaskGroup ofrece una base sólida para trabajo asíncrono supervisado. El inicio ansioso añade control útil para corrutinas que suelen terminar sin I/O. Puede reducir overhead, pero también vuelve observable el orden de creación y puede adelantar excepciones.

Consulta la documentación oficial de tareas asyncio y la PEP 654 sobre grupos de excepciones. Valida el recurso con la versión exacta desplegada y úsalo solo donde las pruebas y las métricas demuestren que compensa.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Desarrolladora trabajando con tipado estático y typing.ReadOnly en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    typing.ReadOnly: campos de solo lectura en TypedDict

    Aprende typing.ReadOnly en Python para declarar claves de solo lectura en TypedDict y crear contratos de datos más seguros.

    Ler mais

    Tempo de leitura: 6 minutos
    22/09/2026
    Código Python que representa argumentos posicionales con functools.Placeholder
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    functools.Placeholder: argumentos medios en partial

    Aprende functools.Placeholder en Python para reservar argumentos intermedios en partial y crear callbacks y adaptadores más claros.

    Ler mais

    Tempo de leitura: 6 minutos
    22/09/2026
    Código Python con aviso de API obsoleta usando warnings.deprecated
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    warnings.deprecated: marca APIs obsoletas

    Aprende warnings.deprecated en Python para marcar APIs obsoletas, orientar migraciones e integrar avisos con tipado, pruebas, documentación y CI.

    Ler mais

    Tempo de leitura: 6 minutos
    21/09/2026
    Ingeniero de software monitorizando la ejecución de código Python con sys.monitoring
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    sys.monitoring: profiling y observabilidad en Python

    Aprende sys.monitoring en Python para crear profilers, cobertura, depuración y observabilidad con eventos selectivos y overhead controlado.

    Ler mais

    Tempo de leitura: 8 minutos
    21/09/2026
    Código Python y template strings
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Template strings: interpolación estructurada en Python

    Aprende cómo las template strings conservan interpolaciones para una renderización estructurada.

    Ler mais

    Tempo de leitura: 8 minutos
    20/09/2026
    Código Python medido para analizar rendimiento con perf_counter_ns
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    perf_counter_ns: mide rendimiento en nanosegundos

    Aprende a medir rendimiento y latencia con perf_counter_ns en Python usando nanosegundos, repeticiones y buenas prácticas.

    Ler mais

    Tempo de leitura: 4 minutos
    20/09/2026