Ponte Síncrona/Assíncrona
Bases de código reais misturam bibliotecas bloqueantes com frameworks assíncronos. Faça a ponte com asyncio.to_thread (3.9+), loop.run_in_executor e asyncio.run apenas em pontos de entrada - nunca chame asyncio.run de dentro de um loop em execução.
Receita
import asyncio
def blocking_io() -> str:
return "done"
async def main() -> None:
result = await asyncio.to_thread(blocking_io)
print(result)Quando usar isso:
- SDK de banco de dados/HTTP síncrono legado em rota FastAPI
- Chamadas de arquivo ou criptografia bloqueantes com pouca CPU
- Invocar assíncrono de um wrapper CLI síncrono (cuidado)
- Migração gradual de Flask para asyncio
- Biblioteca de terceiros sem suporte assíncrono
Exemplo de Trabalho
import asyncio
import time
from concurrent.futures import ThreadPoolExecutor
def slow_sync(n: int) -> int:
time.sleep(0.05)
return n * 2
async def via_to_thread() -> int:
return await asyncio.to_thread(slow_sync, 21)
async def via_executor() -> int:
loop = asyncio.get_running_loop()
with ThreadPoolExecutor(max_workers=2) as pool:
return await loop.run_in_executor(pool, slow_sync, 10)
async def main() -> None:
print(await via_to_thread())
print(await via_executor())
asyncio.run(main())O que isso demonstra:
to_threadagenda a chamável no pool de threads padrãorun_in_executoraceita um executor personalizado para isolamento- O trabalho bloqueante é executado fora da thread do loop de eventos
- O loop permanece responsivo para outras corrotinas
Mergulho Profundo
Matriz de Direção
| De -> Para | Ferramenta |
|---|---|
| bloco sync <- async | to_thread / executor |
| entrada async <- sync | asyncio.run(main()) uma vez |
| meio de voo async <- sync | asyncio.run_coroutine_threadsafe (avançado) |
Vinculado à CPU
- Prefira
ProcessPoolExecutorpara Python com uso intensivo de CPU to_threadnão contorna o GIL para computação
Armadilhas
- asyncio.run dentro de um loop em execução - RuntimeError. Correção: aguarde a corrotina diretamente ou use a ponte thread-safe.
- Inundação de
to_thread- milhares de threads enfileiradas. Correção: semáforo + pool limitado. - ORM bloqueante em cada solicitação - o pool de threads se torna um gargalo. Correção: driver assíncrono nativo.
- Compartilhamento de sessões ORM entre threads - inseguro. Correção: sessão por thread/tarefa.
asyncio.runaninhado em testes - use a fixture de loop único do pytest-asyncio.
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
| Cliente assíncrono nativo | Disponível para dependência | N/A |
| Framework síncrono (WSGI) | Todo o aplicativo bloqueante | Necessidade de E/S concorrente alta |
| anyio | Abstração assíncrona portátil | Política apenas de stdlib |
FAQs
to_thread vs run_in_executor?
to_thread usa o executor padrão - mais simples; executor personalizado quando o isolamento é necessário.
Quantos workers de thread?
Padrão ~ min(32, cpu+4) - menor se as tarefas bloquearem por muito tempo para evitar acúmulo.
Chamar async de REPL sync?
asyncio.run(expr()) apenas em um shell novo - não de dentro de um Jupyter assíncrono sem nest_asyncio (evite em produção).
FastAPI def vs async def?
async def para await; def síncrono é executado no pool de threads automaticamente - ainda prefira E/S assíncrona verdadeira.
Pool de processos a partir do async?
await loop.run_in_executor(process_pool, fn) - observe a serialização e a guarda principal.
contextvars em threads?
contextvars.copy_context().run para propagar o ID da solicitação para o trabalho to_thread.
Views assíncronas do Django?
O ORM ainda é em grande parte síncrono - wrapper sync_to_async com sinalizadores de sensibilidade de thread.
Quando não fazer a ponte?
Se mais de 30% do tempo da rota for no executor - migre para a biblioteca assíncrona ou modelo de implantação de worker síncrono.
time.sleep em thread está ok?
Sim, no worker to_thread; nunca no corpo da corrotina.
Testando pontes?
Simule slow_sync para incrementar um contador; verifique se o loop está responsivo por meio de uma tarefa de heartbeat concorrente.
Relacionado
- threading - primitivas de thread
- concurrent.futures - executores
- Debugging Async Code - detecção de bloqueio
- Async Best Practices - quando migrar da ponte
Versões da Pilha: Esta página foi escrita para Python 3.14.0 (estável 3.14, manutenção 3.13), 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+.