asyncio.Barrier: sincroniza tareas por fases

Publicado el: 08/09/2026
Tempo de leitura: 6 minutos
Equipo sincronizado representando asyncio.Barrier en Python

asyncio.Barrier es una primitiva de sincronización para coordinar varias tareas asíncronas que deben alcanzar el mismo punto antes de continuar. Resulta útil en pipelines por fases, pruebas concurrentes, simulaciones, inicialización paralela, procesamiento por lotes y cualquier diseño donde varias corutinas independientes necesitan terminar una etapa juntas.

Qué hace una barrera asíncrona

La barrera se crea con el número esperado de participantes. Cada tarea ejecuta await barrier.wait() al terminar su etapa. Las que llegan primero quedan suspendidas. Cuando llega la última participante, todas son liberadas y la barrera queda preparada para otra ronda.

import asyncio

async def worker(nombre, barrier):
    print(nombre, "preparando")
    await asyncio.sleep(1)
    print(nombre, "esperando")
    await barrier.wait()
    print(nombre, "siguiente fase")

async def main():
    barrier = asyncio.Barrier(3)
    await asyncio.gather(
        worker("A", barrier),
        worker("B", barrier),
        worker("C", barrier),
    )

asyncio.run(main())

Ningún worker entra en la fase siguiente antes de que los tres lleguen a la barrera. La espera no bloquea el hilo del event loop, por lo que otras corutinas listas pueden seguir ejecutándose.

Cuándo usar asyncio.Barrier

La barrera encaja cuando el trabajo se organiza en rondas. Una aplicación de datos puede consultar varias fuentes en paralelo, normalizar cada respuesta y exigir que todas estén listas antes de comparar resultados. Una simulación puede actualizar todos los agentes y esperar antes de calcular el siguiente estado. Una prueba puede alinear varios clientes exactamente en el punto donde se desea reproducir una condición de carrera.

También sirve durante una inicialización paralela. La conexión con la base de datos, la carga de configuración y la preparación de caché pueden ejecutarse al mismo tiempo, mientras que el servidor solo debe aceptar tráfico cuando las tres tareas hayan terminado.

Un punto de sincronización reutilizable

Después de liberar un grupo completo, asyncio.Barrier vuelve a estar disponible. Esto facilita algoritmos iterativos:

async def participante(indice, barrier):
    for ronda in range(3):
        await ejecutar_etapa(indice, ronda)
        await barrier.wait()
        await combinar_resultados(indice, ronda)
        await barrier.wait()

El ejemplo contiene dos sincronizaciones por ronda. La primera confirma que terminó el trabajo individual y la segunda impide que una tarea comience la siguiente ronda antes de finalizar la consolidación.

Valor devuelto por wait

wait() devuelve un entero distinto a cada participante, normalmente entre cero y parties - 1. El valor permite elegir una sola tarea para ejecutar una acción única:

posicion = await barrier.wait()
if posicion == 0:
    print("Una participante registra el final de la fase")

No conviene asumir que una corutina específica recibirá siempre el cero. El valor representa liderazgo temporal para esa ronda, no una identidad estable.

Uso como gestor de contexto asíncrono

La barrera también puede utilizarse con async with cuando la sincronización pertenece claramente a un bloque:

async with barrier:
    await procesar_fase()

Esta forma puede mejorar la lectura, pero no cambia el requisito fundamental: todas las participantes esperadas deben entrar en la barrera.

Cancelación, barreras rotas y recuperación

La barrera depende de que todas las tareas lleguen al punto de encuentro. Si una falla, es cancelada o termina antes de llamar a wait(), las demás pueden esperar indefinidamente. Por eso el código debe definir timeouts y una estrategia de cancelación.

try:
    await asyncio.wait_for(barrier.wait(), timeout=5)
except TimeoutError:
    await barrier.abort()

abort() marca la barrera como rota y hace que las tareas actuales o futuras reciban una excepción. reset() la devuelve al estado vacío. La recuperación debe coordinarse para que ninguna corutina interprete una ronda interrumpida como exitosa.

Barrier frente a Event, Lock y Semaphore

asyncio.Event comunica que una condición se cumplió, pero no cuenta cuántas tareas llegaron. asyncio.Lock garantiza exclusión mutua alrededor de datos compartidos. asyncio.Semaphore limita el acceso simultáneo a un recurso. La barrera no protege datos ni limita concurrencia: alinea un número fijo de participantes en un punto común.

También puedes estudiar asyncio.Runner en Python, queue.SimpleQueue en Python, contextvars en Python y TopologicalSorter en Python.

Reglas para evitar bloqueos

El número de participantes debe coincidir exactamente con las tareas que llamarán a wait(). Crear una barrera para cinco y ejecutar solo cuatro produce una espera infinita. Tampoco conviene compartir una barrera entre grupos independientes, porque tareas de rondas distintas podrían completar el mismo contador.

Coloca la sincronización cerca de la fase que representa. Una barrera escondida en una función genérica dificulta comprender por qué el sistema espera. Usa nombres como barrera_inicio, barrera_lote o barrera_ronda.

Los errores anteriores a la barrera deben propagarse. Un patrón robusto consiste en ejecutar las participantes dentro de asyncio.TaskGroup, cancelar el grupo cuando una tarea falla y abortar la barrera para liberar con error a quienes estén esperando.

Pruebas concurrentes deterministas

Las barreras son especialmente útiles en pruebas porque reemplazan cálculos de tiempo poco fiables. En lugar de añadir varios sleep() y esperar que las tareas se solapen, cada corutina informa su llegada mediante la barrera. Así la prueba controla el momento exacto en que las operaciones competidoras pueden avanzar.

Añade siempre timeout en las pruebas. Un fallo de implementación debe producir un error claro, no congelar toda la suite. Las propiedades de la barrera ayudan en diagnósticos, pero la lógica de producción no debe depender de observaciones que pueden cambiar inmediatamente.

Ejemplo de procesamiento por fases

import asyncio

async def procesar(nombre, barrier):
    for lote in range(2):
        print(nombre, "leyendo", lote)
        await asyncio.sleep(0.2)

        lider = await barrier.wait()
        if lider == 0:
            print("todos leyeron el lote", lote)

        await barrier.wait()

async def main():
    barrier = asyncio.Barrier(3)
    async with asyncio.TaskGroup() as grupo:
        for numero in range(3):
            grupo.create_task(
                procesar(f"worker-{numero+1}", barrier)
            )

asyncio.run(main())

El primer encuentro garantiza que todos los workers leyeron el lote. Una participante elegida registra la finalización. El segundo encuentro evita que una tarea rápida avance al lote siguiente mientras todavía se realiza la acción compartida.

Cargas de trabajo dinámicas

Una barrera funciona mejor con un conjunto fijo de participantes. Si los workers entran y salen dinámicamente, una cola, evento, condición o coordinador explícito puede ser más adecuado. Recrear barreras constantemente alrededor de un pool cambiante suele producir una lógica frágil.

Cuando algunas tareas sean opcionales, decide la membresía antes de iniciar la ronda. Crea la barrera con el número exacto de participantes seleccionadas. Si solo existe una tarea, considera si la sincronización aporta algo o si una llamada directa es más clara.

Consideraciones de rendimiento

Una barrera es ligera frente a bloquear hilos, pero la sincronización siempre introduce coordinación. No la coloques dentro de operaciones diminutas solo para que todas las tareas avancen al mismo ritmo. Permite que el trabajo independiente continúe libremente y sincroniza únicamente donde la corrección exige un límite compartido.

Cada ronda queda condicionada por la participante más lenta. Si un worker tarda siempre más, los demás pasarán tiempo esperando. La barrera hace visible ese desequilibrio, pero no lo resuelve. Siguen siendo necesarios profiling, partición y balanceo de carga.

Conclusión

asyncio.Barrier ofrece una forma clara de coordinar corutinas a través de fases reutilizables. Suspende tareas sin bloquear el event loop, puede elegir una participante y admite operaciones de aborto y reset. Úsala cuando un grupo fijo deba alcanzar el mismo punto antes de continuar. Para información oficial, consulta la documentación de sincronización de asyncio y la documentación oficial de tareas.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Desarrollador creando modelos con dataclasses.KW_ONLY en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    dataclasses.KW_ONLY: exige argumentos con nombre

    Aprende dataclasses.KW_ONLY en Python para exigir argumentos con nombre, evitar llamadas ambiguas y evolucionar APIs con seguridad.

    Ler mais

    Tempo de leitura: 6 minutos
    08/09/2026
    Desarrollador usando operator.methodcaller en código Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    operator.methodcaller: llama métodos en pipelines

    Aprende operator.methodcaller en Python para map, sorted, callbacks, argumentos y pipelines declarativos claros y reutilizables.

    Ler mais

    Tempo de leitura: 5 minutos
    07/09/2026
    Carpetas y directorios recorridos con pathlib.Path.walk en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pathlib.Path.walk: recorre y filtra directorios

    Aprende a recorrer directorios con pathlib.Path.walk en Python, filtrar archivos, omitir carpetas y evitar errores comunes.

    Ler mais

    Tempo de leitura: 6 minutos
    07/09/2026
    Código Python validado con enum.verify
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    enum.verify: valida reglas de Enum en Python

    Aprende enum.verify en Python para validar valores únicos, secuencias continuas y flags con nombres mediante reglas explícitas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/09/2026
    Grafo de dependencias y flujo de tareas con TopologicalSorter en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    TopologicalSorter: ordena dependencias sin ciclos

    Aprende TopologicalSorter en Python para ordenar dependencias, detectar ciclos y ejecutar pipelines secuenciales o paralelos con seguridad.

    Ler mais

    Tempo de leitura: 6 minutos
    06/09/2026
    Carpetas y directorios que representan os.fwalk en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    os.fwalk en Python: recorre directorios

    Aprende os.fwalk en Python para recorrer directorios con descriptores, reducir condiciones de carrera y manipular archivos con mayor seguridad.

    Ler mais

    Tempo de leitura: 5 minutos
    05/09/2026