30 Regras Assíncronas
Trinta regras para escrever código asyncio correto e performático em Python 3.14. Violar estas regras causa bugs sutis que só aparecem sob carga.
Receita
Revise ao adicionar
async defa qualquer módulo. Emparelhe com testes pytest-asyncio.
Quando usar isto:
- Construindo serviços FastAPI ou workers assíncronos
- Migrando código síncrono para asyncio
- Depurando congelamentos do event loop ou vazamentos de tarefas
Mergulho Profundo
Fundamentos do Event Loop (1-10)
- Um event loop por thread - nunca chame
asyncio.run()dentro de um loop em execução. - Use
asyncio.run()como entrada principal - apenas o script de nível superior e o runner de teste síncrono. - Prefira
asyncio.TaskGroup(3.14) - concorrência estruturada em vez decreate_taskpuro. - Aguarde toda corrotina - corrotinas não aguardadas emitem
RuntimeWarninge não fazem nada. - Não misture APIs síncronas e assíncronas -
requestsem blocosasync defbloqueia o loop. - Use
asyncio.to_thread()para I/O bloqueante - quando nenhuma biblioteca assíncrona existir. - Instale pytest-asyncio -
asyncio_mode = "auto"em pyproject.toml. - Fixtures assíncronas para configuração assíncrona - conexões de banco de dados, clientes httpx.
- Cancele tarefas no desligamento - trate
asyncio.CancelledErrorgraciosamente. - Defina timeouts em chamadas de rede -
asyncio.wait_for()ou httpxtimeout=.
Padrões de Concorrência (11-20)
- asyncio para I/O, não CPU - trabalho de CPU vai para
ProcessPoolExecutor. - Limite a concorrência com Semaphore -
async with sem:limita requisições paralelas. - Use
asyncio.gatherpara tarefas independentes -return_exceptions=Truepara falha parcial. - Use
asyncio.Queuepara produtor-consumidor - filas limitadas evitam estouro de memória. - Evite estado mutável compartilhado - passe dados via argumentos; use locks se compartilhado.
- Pools de conexão também são assíncronos - um
AsyncClientou pool por app, não por requisição. - Inicialize recursos assíncronos preguiçosamente no lifespan - FastAPI
lifespanou@app.on_eventstartup. - Tarefas em segundo plano devem tratar erros - registre exceções em tarefas; não dispare e esqueça silenciosamente.
- Prefira gerenciadores de contexto assíncronos -
async with session.begin():para limites de transação. - Transmita respostas grandes -
StreamingResponseem vez de carregar o corpo completo na memória.
Armadilhas e Depuração (21-30)
- Nunca use
time.sleep()em async - useawait asyncio.sleep(). - Cuidado com
@lru_cacheem funções assíncronas - cacheia objetos corrotina, não resultados. Useasync_lru. - DNS é bloqueante - use
aiodnsou execute o resolvedor em uma thread para apps de alta concorrência. - Handshake SSL é bloqueante - use clientes HTTP assíncronos (httpx, aiohttp), não urllib.
- Fique atento a vazamentos de tarefas - rastreie tarefas em segundo plano; cancele na conclusão da requisição.
- Teste caminhos de cancelamento - desconexão do cliente deve cancelar o trabalho do servidor.
- Registre exceções de tarefas -
task.add_done_callbackpara registrar erros não tratados. - Não reutilize loops fechados - crie um loop novo por teste ou use pytest-asyncio.
- Free-threaded 3.14 requer auditoria - estado compartilhado sem GIL requer locks.
- Profile sob carga concorrente - profiling de requisição única perde bugs de contenção.
Armadilhas
- Chamada bloqueante em rota assíncrona - o servidor inteiro trava. Correção:
await asyncio.to_thread(blocking_fn). create_tasksem referência - tarefa coletada pelo garbage collector em meio à execução. Correção: armazene a referência da tarefa ou use TaskGroup.asyncio.gathersem tratamento de erro - uma falha cancela todas. Correção:return_exceptions=Trueou try/except explícito por tarefa.- Cliente assíncrono global - conexões obsoletas após fork. Correção: crie por processo no lifespan; feche no desligamento.
- Testando com
asyncio.runem cada teste - lento e conflitos de loop. Correção: pytest-asyncio com modo auto.
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
| threading | Apenas bibliotecas bloqueantes | I/O de alta concorrência |
| multiprocessing | Paralelismo limitado por CPU | Estado compartilhado necessário |
| Sync FastAPI + workers | Equipe não familiarizada com async | I/O intensivo com drivers assíncronos disponíveis |
FAQs
Quando devo usar async?
Quando o app é limitado por I/O e existem bibliotecas assíncronas para seu banco de dados, HTTP e cache.
FastAPI deve ser async?
Não. Rotas síncronas funcionam via thread pool. Rotas assíncronas são preferíveis com drivers de banco de dados assíncronos.
TaskGroup vs gather?
TaskGroup (3.11+) para concorrência estruturada com cancelamento automático em caso de falha.
Como depuro um servidor congelado?
Use py-spy no processo. Procure por bloqueios síncronos em pilhas de chamadas assíncronas.
Padrão async sqlalchemy?
async with async_session() as session: por requisição via dependência FastAPI.
Django pode ser async?
Django 5.x suporta views assíncronas. O suporte async do ORM é parcial; avalie por caso de uso.
Quantas tarefas concorrentes?
Comece com um semáforo de 10-50. Ajuste com base nos limites de conexão e memória.
E sobre trio?
anyio faz a ponte entre asyncio e trio. O ecossistema FastAPI é nativo asyncio.
Geradores async?
async for com geradores assíncronos. Feche com aclose() em blocos finally.
Como migro de sync para async?
Um endpoint por vez. Troque o driver síncrono por um driver assíncrono. Teste sob carga.
Relacionados
- Async & Concorrência para Throughput - padrões de performance
- Testando Código Async - testes async
- Concorrência - GIL e modelos
- 40 Regras de Design de API - regras de API assíncronas
Versões de Stack: Esta página foi escrita para Python 3.14.0, FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, e uv 0.6+.