asyncio.eager_task_factory: reduza overhead de tarefas

Publicado em: 14/09/2026
Tempo de leitura: 4 minutos
Código assíncrono representando asyncio.eager_task_factory no Python

asyncio.eager_task_factory permite que corrotinas comecem a executar imediatamente no momento em que uma tarefa é criada, antes de serem agendadas no loop de eventos. Essa mudança pode reduzir overhead em operações muito curtas, mas também altera a ordem de execução esperada e exige cuidado com efeitos colaterais, exceções e testes.

O que é execução eager

No comportamento tradicional do asyncio, criar uma tarefa com asyncio.create_task() agenda a corrotina para executar em uma próxima oportunidade do event loop. Com uma task factory eager, o início acontece de forma síncrona durante a criação da tarefa. Se a corrotina terminar sem precisar suspender, ela pode concluir sem sequer entrar na fila normal do loop.

Esse comportamento é útil para corrotinas que frequentemente retornam de cache, validam dados em memória ou concluem rapidamente. Em sistemas com milhares de chamadas pequenas, evitar o custo de agendamento pode representar uma melhoria mensurável. Porém, o ganho não deve ser presumido: ele precisa ser confirmado com benchmarks próximos da carga real.

Como ativar a task factory

import asyncio

async def buscar_cache(chave, cache):
    if chave in cache:
        return cache[chave]
    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"}
    tarefa = asyncio.create_task(buscar_cache("curso", cache))
    resultado = await tarefa
    print(resultado)

asyncio.run(main())

A configuração é aplicada ao loop atual. A partir daí, tarefas criadas nesse loop usam a factory configurada. Em aplicações maiores, é recomendável centralizar essa decisão na inicialização para evitar ambientes inconsistentes.

Quando a corrotina entra no event loop

Se a corrotina executada de forma eager encontra um await que realmente precisa aguardar, a tarefa passa a ser agendada normalmente. Portanto, o comportamento eager acelera principalmente o trecho inicial e as corrotinas que conseguem terminar sem bloqueio assíncrono.

Esse detalhe é importante em funções que consultam cache primeiro e só depois fazem I/O. Uma resposta em cache pode terminar imediatamente, enquanto uma ausência de cache segue pelo fluxo normal do loop.

Diferenças na ordem de execução

A principal mudança semântica está na ordem. Código que antes criava uma tarefa e executava outra instrução antes de a tarefa começar pode deixar de se comportar assim.

import asyncio

async def trabalho():
    print("corrotina iniciou")
    return 42

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

    print("antes")
    tarefa = asyncio.create_task(trabalho())
    print("depois")
    print(await tarefa)

asyncio.run(main())

Com execução eager, a mensagem da corrotina pode aparecer antes de depois. Isso pode expor dependências ocultas de ordem, especialmente em código com logs, métricas, mutação de estado compartilhado ou callbacks.

Tratamento de exceções

Exceções também podem surgir mais cedo. Uma corrotina que falha antes do primeiro bloqueio pode produzir o erro durante a criação e execução inicial da tarefa. Por isso, testes devem cobrir tanto caminhos que concluem imediatamente quanto caminhos que suspendem.

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

Em aplicações com grupos de tarefas, avalie como o comportamento interage com asyncio.Runner, cancelamento e encerramento limpo. Também vale revisar padrões de sincronização descritos em asyncio.Barrier.

Uso com caches

O caso clássico é uma função assíncrona que consulta um cache local antes de acessar rede ou banco de dados. Quando há acerto, a corrotina termina de forma síncrona; quando não há, ela entra no fluxo assíncrono normal.

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

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

Esse padrão pode reduzir overhead, mas o benefício depende da taxa de acerto e do volume. Faça medições antes e depois, usando dados representativos.

Riscos de estado compartilhado

Como a tarefa pode executar imediatamente, qualquer alteração em listas, dicionários, contadores ou objetos globais pode ocorrer antes do esperado. Prefira funções com efeitos colaterais explícitos, estados imutáveis quando possível e mecanismos de sincronização claros.

Para filas entre threads, veja queue.SimpleQueue. Para processamento paralelo de CPU, consulte InterpreterPoolExecutor.

Benchmark correto

Um benchmark útil deve separar três cenários: conclusão imediata, suspensão curta e I/O dominante. Em tarefas dominadas por rede, a diferença pode ser irrelevante. Em milhões de chamadas de cache, pode ser significativa.

import asyncio
import time

async def imediato():
    return 1

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

Repita o teste, descarte aquecimento, compare versões do Python e observe uso de CPU e memória.

Testes recomendados

Crie testes que verifiquem ordem de logs, exceções antes do primeiro await, cancelamento, callbacks de conclusão e resultados de cache. Não teste apenas o valor final: teste também a sequência de eventos quando ela fizer parte do contrato.

Boas práticas

Ative a factory em um ponto central, documente a mudança, não dependa de ordem acidental, mantenha benchmarks e use observabilidade. O recurso deve ser tratado como otimização semântica, não apenas como uma opção de desempenho.

A documentação oficial de tarefas asyncio descreve a API e seus detalhes. Para compreender o modelo geral de concorrência, consulte também a documentação do asyncio.

Conclusão

asyncio.eager_task_factory pode reduzir o custo de tarefas curtas e caminhos de cache, mas muda quando a corrotina começa. Use o recurso quando houver ganho comprovado, testes sólidos e código preparado para uma ordem de execução mais imediata.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Desenvolvedor trabalhando com timestamps UTC e calendar.timegm no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    calendar.timegm: converta UTC para timestamp Unix

    Aprenda calendar.timegm no Python para converter datas UTC em timestamps Unix com segurança, testes e integração com datetime.

    Ler mais

    Tempo de leitura: 6 minutos
    14/09/2026
    Programador analisando código para identificar tipos MIME de arquivos no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    mimetypes.guess_file_type: detecte tipos MIME

    Aprenda mimetypes.guess_file_type no Python para detectar tipos MIME em caminhos, URLs, uploads e respostas HTTP com fallbacks seguros.

    Ler mais

    Tempo de leitura: 6 minutos
    13/09/2026
    Componentes de servidor representando interpretadores Python executando em paralelo
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    InterpreterPoolExecutor: paralelismo real no Python

    Aprenda InterpreterPoolExecutor no Python para executar tarefas CPU-bound em interpretadores isolados com paralelismo real.

    Ler mais

    Tempo de leitura: 6 minutos
    13/09/2026
    Microprocessador representando CPUs disponíveis para um processo Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    os.process_cpu_count: conte CPUs disponíveis

    Aprenda os.process_cpu_count no Python para dimensionar workers conforme as CPUs realmente disponíveis ao processo.

    Ler mais

    Tempo de leitura: 4 minutos
    12/09/2026
    Laptop com código digital representando dados BLOB no SQLite
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    sqlite3.Blob: leia BLOBs sem carregar tudo na memória

    Aprenda sqlite3.Blob no Python para ler e gravar BLOBs em partes, reduzir memória e trabalhar com dados binários no SQLite.

    Ler mais

    Tempo de leitura: 5 minutos
    12/09/2026
    Análise estatística para random.binomialvariate no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    random.binomialvariate: simule distribuições binomiais

    Aprenda random.binomialvariate no Python para simular sucessos, validar probabilidades e analisar cenários binomiais com clareza.

    Ler mais

    Tempo de leitura: 6 minutos
    11/09/2026