Módulo 9 - Async e asyncio
Async ou threads: como escolher
10 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026
O que você vai aprender
- Comparar async e threads lado a lado.
- Escolher a ferramenta certa conforme o problema.
- Entender por que código bloqueante trava o event loop.
- Fechar o raciocínio de concorrência do curso.
Ouvir o resumo desta aula
Um recap de cerca de 2 minutos na voz do Valim, para ouvir no trânsito ou na academia.
Ler a transcrição do resumo
Resumo da aula: Async ou threads: como escolher.
Os objetivos desta aula. Comparar async e threads lado a lado. Escolher a ferramenta certa conforme o problema. Entender por que código bloqueante trava o event loop. Fechar o raciocínio de concorrência do curso.
Veja o essencial, parte por parte.
Async e threads lado a lado. Ambos servem para I/O bound; nenhum acelera cálculo puro (isso é com processos).
O perigo de bloquear o loop. Dentro de async, use as versões assíncronas: asyncio.sleep no lugar de time.sleep, clientes assíncronos de rede e banco.
Fechando o raciocínio de concorrência. Junte tudo e o mapa de decisão fica claro.
Esse foi o resumo do essencial. Para se aprofundar, leia a aula completa e responda os exercícios.
Async e threads lado a lado
Depois de dois módulos, a pergunta natural é: quando usar cada um? Comece pelo que os une. Async e threads atacam o mesmo problema, o I/O bound, aquele em que o programa espera muito por rede, disco ou banco. E os dois compartilham a mesma limitação: rodam efetivamente em um thread de trabalho no que toca ao Python, então nenhum quebra o GIL para acelerar cálculo. Para CPU bound, a resposta dos dois módulos é a mesma, processos. A escolha entre async e threads, portanto, só se coloca dentro do território do I/O, e aí entram as diferenças de escala e de estilo.
Prefira threads quando
- O código já usa bibliotecas bloqueantes comuns
- São poucas ou moderadas tarefas concorrentes
- Você quer paralelizar código existente com pouco esforço
- A simplicidade de encaixar importa mais que a escala
Prefira async quando
- Há milhares de conexões concorrentes
- Você constrói um servidor ou cliente de rede do zero
- Existem bibliotecas assíncronas para o que você chama
- Você quer um modelo previsível, sem travas manuais
O perigo de bloquear o loop
Há uma armadilha que derruba quem começa com async: misturar código bloqueante dentro de uma corrotina. O modelo assíncrono só funciona porque cada tarefa cede o controle nos pontos de await. Se você chama, dentro de uma corrotina, uma operação que não cede o controle, como o time.sleep comum, uma leitura síncrona de arquivo ou uma chamada de rede de uma biblioteca bloqueante, o event loop fica preso naquela chamada. Durante todo esse tempo, nenhuma outra corrotina avança, mesmo que a espera delas já tivesse terminado. O async vira um código sequencial disfarçado, e você perde justamente o que foi buscar.
import asyncio
import time
async def certo(nome):
await asyncio.sleep(1) # cede o loop: outras corrotinas avancam
return nome
async def errado(nome):
time.sleep(1) # BLOQUEIA o loop: tudo para durante 1s
return nome
async def main():
# com 'certo', gather termina em ~1s; com 'errado', em ~3s
inicio = time.perf_counter()
await asyncio.gather(certo("A"), certo("B"), certo("C"))
print(f"certo: {time.perf_counter() - inicio:.1f}s")
asyncio.run(main())asyncio.sleep cede o loop; time.sleep dentro de async bloqueia tudo e mata a concorrência.
Fechando o raciocínio de concorrência
Junte tudo e o mapa de decisão fica claro. Primeiro pergunte se a tarefa é I/O bound ou CPU bound. Se for CPU bound, cálculo pesado, nem async nem threads ajudam; use processos, com o ProcessPoolExecutor do módulo anterior. Se for I/O bound, você está no território de async e threads. Aí a segunda pergunta é sobre escala e código: para muitas conexões, servidores de rede e projetos novos com bibliotecas assíncronas, prefira async, que escala melhor e tem modelo previsível. Para encaixar concorrência em código já existente, com bibliotecas bloqueantes comuns e um volume moderado de tarefas, threads costumam ser o caminho mais simples.
| Pergunta | Resposta | Ferramenta |
|---|---|---|
| É cálculo pesado (CPU)? | Sim | Processos (ProcessPoolExecutor) |
| É espera de I/O e há muitas conexões? | Sim | async com asyncio |
| É I/O em código bloqueante existente? | Sim | Threads (ThreadPoolExecutor) |
| A operação é rápida e única? | Sim | Nenhum: código comum basta |
Primeiro I/O ou CPU; depois, escala e estilo do código decidem entre async e threads.
Uma última recomendação, no espírito do curso: meça antes de escolher. Concorrência acrescenta complexidade, e nem todo programa precisa dela. Se uma tarefa roda rápido o bastante em código comum, deixe simples. Quando o volume de I/O crescer e o tempo de espera pesar, aí sim vale trazer async ou threads, guiado por medição, não por palpite. Com este módulo, você fecha a frente de concorrência do curso e segue para a orientação a objetos avançada, onde o foco volta ao projeto das suas próprias estruturas.
Teste rápido
Por que usar time.sleep dentro de uma corrotina é um problema?
Perguntas frequentes
- Async é sempre melhor que threads?
- Não. Async escala melhor para muitas conexões e tem um modelo previsível, mas exige que as bibliotecas usadas sejam assíncronas e que o código todo respeite os pontos de await. Threads encaixam concorrência em código bloqueante existente com menos esforço. A melhor escolha depende do projeto, do volume e das bibliotecas disponíveis.
- Posso misturar async e threads no mesmo programa?
- Pode, com cuidado. Um padrão comum é rodar uma função bloqueante fora do event loop, delegando-a a um executor, para não travar o loop. Combinar os dois mundos exige entender a fronteira entre eles; para começar, é mais seguro escolher um modelo por vez até dominar cada um.
- Se eu tiver uma biblioteca só bloqueante, dá para usar em async?
- Dá, mas não chamando direto na corrotina, o que travaria o loop. O caminho é executá-la fora do loop, em um executor de threads ou processos, e aguardar o resultado. Assim o event loop continua livre para tocar as outras corrotinas enquanto a função bloqueante roda em outro lugar.
- E se a tarefa for cálculo pesado, async resolve?
- Não. Async roda em um único thread; um cálculo pesado dentro de uma corrotina prende o event loop e trava tudo. Para CPU bound, a resposta é a mesma dos dois módulos: paralelismo com processos, via ProcessPoolExecutor. Async e threads são para espera de I/O, não para conta.
- Por onde começo se nunca usei concorrência?
- Comece medindo se você realmente precisa. Se o programa já é rápido, mantenha simples. Se há muita espera de I/O, o ThreadPoolExecutor costuma ser o passo mais fácil para um primeiro ganho. Quando o volume de conexões crescer muito, aí o async passa a compensar o esforço extra de reescrever no modelo assíncrono.
Fontes
Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.