asyncio.eager_task_factory: reduce overhead de tareas

Publicado el: 14/09/2026
Tempo de leitura: 5 minutos
Código asíncrono que representa asyncio.eager_task_factory en Python

asyncio.eager_task_factory permite que una corrutina comience a ejecutarse inmediatamente cuando se crea una tarea, antes de pasar por la planificación normal del event loop. Esto puede reducir overhead en operaciones muy cortas, especialmente accesos a caché y validaciones en memoria, pero también modifica el orden de ejecución y exige cuidado con efectos secundarios, excepciones, cancelación y pruebas.

Qué significa ejecución eager

En el modelo tradicional de asyncio, llamar a asyncio.create_task() programa la corrutina para una próxima vuelta del event loop. Con una task factory eager, la corrutina empieza de forma síncrona durante la creación de la tarea. Si termina sin bloquearse, puede completarse sin entrar en la cola normal del planificador.

Este comportamiento puede beneficiar aplicaciones que crean muchas corrutinas pequeñas. Ejemplos comunes son búsquedas en caché, validaciones, cálculos memoizados y wrappers que con frecuencia devuelven un resultado inmediato. Aun así, la optimización debe medirse con cargas reales.

Cómo activar la factory

import asyncio

async def leer_cache(clave, cache):
    if clave in cache:
        return cache[clave]
    await asyncio.sleep(0.01)
    return None

async def main():
    loop = asyncio.get_running_loop()
    loop.set_task_factory(asyncio.eager_task_factory)

    cache = {"curso": "Python"}
    tarea = asyncio.create_task(leer_cache("curso", cache))
    resultado = await tarea
    print(resultado)

asyncio.run(main())

La configuración afecta al loop actual. En aplicaciones grandes conviene definirla en un único punto de inicialización y documentarla para evitar diferencias silenciosas entre componentes.

Cuándo vuelve a la planificación normal

Si la corrutina llega a un await que no puede completarse inmediatamente, la tarea pasa al event loop de manera normal. Por tanto, la ejecución eager beneficia sobre todo la parte síncrona inicial y las corrutinas que terminan sin suspensión.

Esto resulta útil en funciones que consultan primero una caché y solo después realizan I/O. Un acierto puede finalizar inmediatamente, mientras que un fallo de caché continúa por el flujo asíncrono habitual.

Cambios en el orden de ejecución

La diferencia semántica más importante es el orden. Código que antes creaba una tarea y ejecutaba otra instrucción antes de que la corrutina comenzara puede dejar de comportarse así.

import asyncio

async def trabajo():
    print("la corrutina comenzó")
    return 42

async def main():
    loop = asyncio.get_running_loop()
    loop.set_task_factory(asyncio.eager_task_factory)

    print("antes")
    tarea = asyncio.create_task(trabajo())
    print("después")
    print(await tarea)

asyncio.run(main())

Con ejecución eager, el mensaje de la corrutina puede aparecer antes de después. Esto puede revelar dependencias ocultas de orden en logs, métricas, mutaciones de estado, callbacks y preparación de recursos.

Excepciones más tempranas

Una corrutina que falla antes de su primer await bloqueante puede producir el error durante la ejecución inicial. Las pruebas deben cubrir tanto caminos que terminan inmediatamente como caminos que se suspenden.

async def validar(valor):
    if valor < 0:
        raise ValueError("valor inválido")
    return valor

En sistemas con grupos de tareas o cierre estructurado, revisa cómo estos errores interactúan con cancelación y limpieza. Consulta asyncio.Runner para comprender el ciclo de vida del loop y asyncio.Barrier para sincronización por fases.

Caso de uso con caché

El escenario clásico es una función asíncrona que comprueba estado local antes de realizar una petición externa.

async def obtener_usuario(user_id, cache, cliente):
    if user_id in cache:
        return cache[user_id]

    respuesta = await cliente.get(f"/users/{user_id}")
    cache[user_id] = respuesta
    return respuesta

Si la tasa de aciertos es alta, la ejecución eager puede evitar costes repetidos de planificación. Si casi todas las llamadas llegan a la red, la mejora puede ser mínima.

Riesgos con estado compartido

Como la corrutina puede ejecutarse inmediatamente, cambios en listas, diccionarios, contadores o variables de contexto pueden ocurrir antes de lo esperado. Es preferible usar transiciones explícitas, valores inmutables cuando sea viable y mecanismos de sincronización probados.

Para comunicación FIFO segura entre hilos, revisa queue.SimpleQueue. Para paralelismo de tareas CPU-bound, consulta InterpreterPoolExecutor.

Benchmark correcto

Un benchmark útil debe separar finalización inmediata, suspensión breve y tareas dominadas por I/O. En cargas de red puede no existir diferencia visible, mientras que millones de aciertos de caché sí pueden mostrar una mejora.

import asyncio
import time

async def inmediato():
    return 1

async def medir(cantidad):
    inicio = time.perf_counter()
    tareas = [asyncio.create_task(inmediato()) for _ in range(cantidad)]
    await asyncio.gather(*tareas)
    return time.perf_counter() - inicio

Repite las mediciones, descarta calentamiento, compara versiones de Python y observa CPU, memoria, throughput y latencia de cola.

Estrategia de pruebas

Prueba orden de logs, callbacks, excepciones antes del primer await, aciertos y fallos de caché, cancelación, timeouts y limpieza. No verifiques solo el resultado final cuando la secuencia sea parte del contrato.

Cancelación

Una tarea que termina de forma eager puede finalizar antes de que el código de cancelación actúe. Una tarea que se suspende sigue las reglas normales. Por eso es importante probar caminos rápidos y lentos.

Observabilidad

Las métricas y trazas pueden observar eventos en otro orden. Crea spans y contexto antes de construir la tarea cuando deban incluir el trabajo eager. Las herramientas de bajo nivel no deberían asumir que toda tarea aparece primero en la cola regular.

Despliegue en producción

Introduce la factory detrás de una opción de configuración, compara latencias y errores, documenta el cambio semántico y despliega gradualmente cuando existan callbacks complejos o estado compartido.

Buenas prácticas

Activa la factory de manera centralizada, evita dependencias accidentales de orden, mide cargas reales, conserva garantías de limpieza y mantén pruebas de regresión. Debe tratarse como una optimización semántica, no como un simple interruptor de rendimiento.

La documentación oficial de tareas asyncio explica la API y sus advertencias. La documentación general de asyncio describe el event loop y sus primitivas.

Conclusión

asyncio.eager_task_factory puede reducir el coste de corrutinas cortas y flujos con caché, pero cambia el momento exacto en que el código comienza. Úsala con beneficios medidos, pruebas sólidas y una arquitectura preparada para ejecución anticipada.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Desarrollador trabajando con timestamps UTC y calendar.timegm en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    calendar.timegm: convierte UTC a timestamp Unix

    Aprende calendar.timegm en Python para convertir fechas UTC en timestamps Unix y evitar errores de zona horaria y unidades.

    Ler mais

    Tempo de leitura: 6 minutos
    14/09/2026
    Programador analizando código para identificar tipos MIME de archivos con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    mimetypes.guess_file_type: detecta tipos MIME

    Aprende mimetypes.guess_file_type en Python para detectar tipos MIME en rutas, URLs, uploads y respuestas HTTP con fallbacks seguros.

    Ler mais

    Tempo de leitura: 4 minutos
    13/09/2026
    Componentes de servidor que representan intérpretes Python aislados ejecutándose en paralelo
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    InterpreterPoolExecutor: paralelismo real en Python

    Aprende InterpreterPoolExecutor en Python para tareas CPU-bound, intérpretes aislados, paralelismo real y concurrencia segura.

    Ler mais

    Tempo de leitura: 6 minutos
    13/09/2026
    Microprocesador que representa las CPU disponibles para un proceso Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    os.process_cpu_count: cuenta CPUs disponibles

    Aprende os.process_cpu_count en Python para dimensionar workers según las CPU disponibles para el proceso.

    Ler mais

    Tempo de leitura: 4 minutos
    12/09/2026
    Portátil con código digital que representa datos BLOB en SQLite
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    sqlite3.Blob: lee BLOBs sin cargar todo en memoria

    Aprende sqlite3.Blob en Python para leer y escribir BLOBs por partes, reducir memoria y manejar datos binarios en SQLite.

    Ler mais

    Tempo de leitura: 5 minutos
    12/09/2026
    Análisis estadístico para random.binomialvariate en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    random.binomialvariate: simula resultados binomiales

    Aprende random.binomialvariate en Python para simular éxitos, validar probabilidades y analizar escenarios binomiales con ejemplos.

    Ler mais

    Tempo de leitura: 5 minutos
    11/09/2026