asyncio.Barrier: sincronize tarefas por etapas

Publicado em: 08/09/2026
Tempo de leitura: 5 minutos
Equipe sincronizada representando asyncio.Barrier no Python

O asyncio.Barrier é uma primitiva de sincronização criada para coordenar várias tarefas assíncronas que precisam alcançar o mesmo ponto antes de continuar. Em aplicações com pipelines, processamento em fases, simulações, testes concorrentes ou inicialização paralela de serviços, é comum que várias corrotinas executem trabalhos independentes e depois precisem esperar umas pelas outras. A barreira resolve exatamente esse problema sem bloquear a thread do event loop.

O que é uma barreira assíncrona

Uma barreira recebe o número de participantes esperados. Cada tarefa chama await barrier.wait() ao terminar sua etapa. Enquanto o total definido não chega, as tarefas permanecem suspensas. Quando a última participante alcança a barreira, todas são liberadas praticamente ao mesmo tempo e podem iniciar a fase seguinte.

import asyncio

async def worker(nome, barrier):
    print(f"{nome}: preparando")
    await asyncio.sleep(1)
    print(f"{nome}: aguardando")
    await barrier.wait()
    print(f"{nome}: próxima fase")

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

asyncio.run(main())

Nesse exemplo, nenhuma tarefa entra na próxima fase antes que as três tenham terminado a preparação. Diferente de um bloqueio tradicional, o await permite que o event loop continue executando outras tarefas enquanto cada participante espera.

Quando usar asyncio.Barrier

A barreira é adequada quando o fluxo pode ser dividido em rodadas. Em uma coleta distribuída de dados, por exemplo, cada tarefa consulta uma fonte, normaliza sua resposta e aguarda as demais antes de iniciar uma comparação conjunta. Em testes, várias corrotinas podem ser posicionadas exatamente no mesmo estágio para reproduzir uma condição de concorrência de forma controlada.

Ela também é útil em inicializações paralelas. Imagine três componentes: conexão com banco de dados, carregamento de configuração e preparação de cache. Todos podem iniciar em paralelo, mas o servidor só deve aceitar requisições quando os três estiverem prontos. Uma barreira torna essa intenção explícita.

Barreira reutilizável

Uma característica importante é que asyncio.Barrier pode ser reutilizada. Depois que todos os participantes são liberados, ela volta ao estado inicial e pode coordenar outra rodada. Isso facilita algoritmos iterativos:

async def participante(indice, barrier):
    for rodada in range(3):
        await executar_etapa(indice, rodada)
        await barrier.wait()
        await consolidar_resultado(indice, rodada)
        await barrier.wait()

Nesse desenho existem duas sincronizações por rodada: uma depois do trabalho individual e outra após a consolidação. O número de participantes deve permanecer coerente. Se uma tarefa desaparecer sem tratamento, as outras podem esperar indefinidamente.

Valor retornado por wait

O método wait() retorna um número inteiro diferente para cada participante, normalmente entre zero e parties - 1. Esse retorno permite escolher uma tarefa para realizar uma ação única após a liberação:

posicao = await barrier.wait()
if posicao == 0:
    print("Uma única tarefa registra o fim da fase")

Não dependa de qual corrotina específica receberá o valor zero. Use o retorno apenas para eleger uma participante, sem associar identidade fixa ao índice.

Uso como gerenciador de contexto

A barreira também pode ser usada com async with. Esse formato melhora a leitura quando a sincronização pertence claramente a um bloco:

async with barrier:
    await processar_fase()

O comportamento essencial continua sendo esperar os demais participantes. O formato deve ser adotado quando tornar o código mais claro, e não apenas por estilo.

Estado quebrado e reset

Barreiras dependem da chegada de todas as tarefas. Se uma participante for cancelada, falhar antes de chamar wait() ou abandonar o protocolo, as outras podem ficar presas. Por isso, o código deve definir timeouts, cancelamento e recuperação.

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

O método abort() coloca a barreira em estado quebrado e faz participantes atuais ou futuros receberem uma exceção. Já reset() devolve a barreira ao estado vazio. Essas operações devem ser coordenadas cuidadosamente para que nenhuma tarefa interprete uma rodada interrompida como concluída.

Barrier, Event, Lock e Semaphore

asyncio.Event comunica que uma condição ocorreu e pode liberar várias tarefas, mas não conta quantas chegaram. asyncio.Lock garante exclusão mútua para proteger uma região crítica. asyncio.Semaphore limita quantas tarefas usam um recurso simultaneamente. A barreira, por outro lado, não protege dados nem limita acesso: ela alinha participantes em um ponto de encontro.

Para aprofundar concorrência assíncrona, veja também asyncio.Runner no Python, queue.SimpleQueue no Python, contextvars no Python e TopologicalSorter no Python.

Cuidados de projeto

O número de participantes deve representar exatamente as tarefas que realmente chamarão wait(). Criar uma barreira para cinco tarefas e iniciar apenas quatro causa espera infinita. Também evite compartilhar a mesma barreira entre grupos independentes, porque participantes de rodadas diferentes podem ser contabilizados juntos.

Coloque a sincronização perto da lógica que define a fase. Uma barreira escondida em função genérica dificulta entender por que o sistema espera. Nomeie variáveis conforme o propósito, como barreira_carregamento ou barreira_rodada.

Erros antes da barreira devem ser propagados e tratados. Uma estratégia robusta é executar tarefas dentro de asyncio.TaskGroup, cancelar o grupo quando uma falha ocorrer e abortar a barreira para liberar quem estiver esperando.

Testes confiáveis

Barreiras ajudam a tornar testes concorrentes determinísticos. Em vez de usar vários sleep() e torcer para que as tarefas cheguem ao mesmo ponto, cada corrotina sinaliza sua chegada. O teste controla o momento exato em que todas avançam.

Ainda assim, inclua timeout nos testes. Um erro de implementação não deve deixar a suíte parada. Verifique também os estados e propriedades oferecidos pela barreira, como quantidade de participantes e número de tarefas esperando, sem basear a lógica de produção em observações sujeitas a corrida.

Exemplo com fases de processamento

import asyncio

async def etapa(nome, barrier):
    for lote in range(2):
        print(nome, "lendo", lote)
        await asyncio.sleep(0.2)

        await barrier.wait()

        if nome == "worker-1":
            print("todos terminaram a leitura", lote)

        await barrier.wait()

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

asyncio.run(main())

O primeiro encontro garante que todos leram o lote. O segundo impede que uma tarefa inicie o lote seguinte antes do registro de conclusão. Em código real, a eleição pelo retorno de wait() pode ser preferível à comparação do nome.

Conclusão

asyncio.Barrier é uma solução clara para sincronizar corrotinas em fases. Ela evita bloqueio da thread, pode ser reutilizada e oferece mecanismos de cancelamento, aborto e reset. Use-a quando todas as participantes precisarem atingir o mesmo ponto antes de continuar. Para detalhes oficiais, consulte a documentação de sincronização do Python e o guia oficial de tarefas asyncio.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Desenvolvedor criando modelos com dataclasses.KW_ONLY no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    dataclasses.KW_ONLY: exija argumentos nomeados

    Aprenda dataclasses.KW_ONLY no Python para criar APIs com argumentos nomeados, evitar chamadas ambíguas e evoluir modelos com segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    08/09/2026
    Desenvolvedor usando operator.methodcaller em código Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    operator.methodcaller: chame métodos em pipelines

    Aprenda operator.methodcaller no Python para chamar métodos em map, sorted e pipelines com argumentos e código mais declarativo.

    Ler mais

    Tempo de leitura: 5 minutos
    07/09/2026
    Estrutura de pastas percorrida com pathlib.Path.walk no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pathlib.Path.walk: percorra e filtre diretórios

    Aprenda a percorrer diretórios com pathlib.Path.walk no Python, filtrar arquivos, ignorar pastas e evitar armadilhas comuns.

    Ler mais

    Tempo de leitura: 5 minutos
    07/09/2026
    Código Python validado com enum.verify
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    enum.verify: valide regras de Enum no Python

    Aprenda enum.verify no Python para validar valores únicos, sequências contínuas e flags nomeadas com regras explícitas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/09/2026
    Grafo de dependências e fluxo de tarefas com TopologicalSorter no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    TopologicalSorter: ordene dependências sem ciclos

    Aprenda TopologicalSorter no Python para ordenar dependências, detectar ciclos e executar pipelines sequenciais ou paralelos com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    06/09/2026
    Pastas e diretórios representando os.fwalk no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    os.fwalk no Python: percorra diretórios

    Aprenda os.fwalk no Python para percorrer diretórios com descritores, reduzir condições de corrida e manipular arquivos com mais segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    05/09/2026