Boas Práticas de Concorrência
Regras de segurança e operabilidade para threads, processos e pontes para código assíncrono.
Como Usar Esta Lista
- Aplique antes de aumentar
max_workersou o fan-out de tarefas. - Teste de carga nos caminhos de encerramento - corridas de dados aparecem na implantação/reinicialização.
- Prefira passagem de mensagens; trate memória compartilhada como culpada até que se prove segura.
A - Seleção de Modelo
- Perfilar antes de escolher asyncio vs threads vs processos. Rótulos enganam sem dados de tempo.
- Mantenha trabalho de CPU fora da thread do loop de eventos asyncio. Use
to_threadou pool de processos. - Use ProcessPool para Python puro limitado por CPU em builds com GIL. Não espere aceleração linear com threads.
- Dimensionar pools de threads para limites de concorrência de I/O. Pools de conexão, não
cpu_count() * 5por padrão. - Documente a escolha do modelo no ADR. Mantenedores futuros precisam do motivo.
B - Estado Compartilhado
- Prefira filas a estruturas mutáveis compartilhadas. Produtor/consumidor simplifica o raciocínio.
- Proteja invariantes de múltiplos passos com locks. Operações de bytecode únicas não são transações completas.
- Adquira locks em ordem global fixa. Documente a ordem para prevenir deadlocks.
- Evite singletons globais mutados por workers. Passe contexto explícito ou use
threading.local(). - Use mensagens imutáveis entre estágios. Snapshots, não grafos de objetos vivos.
C - Processos e Pools
- Proteja a entrada de multiprocessing com
if __name__ == "__main__". Necessário em plataformas de spawn. - Mantenha targets de worker como funções de nível superior serializáveis. Sem lambdas em ProcessPool.
- Defina
maxtasksperchildpara extensões C com vazamento de memória. Pools de longa duração reciclam workers. - Limite workers pelo orçamento de memória. RSS × workers devem caber no limite do contêiner.
- Configure o logging no inicializador do worker. Evite manipuladores quebrados herdados.
D - Encerramento e Erros
- Junte threads com timeout durante o encerramento. Registre os atrasados; evite daemons no meio de seções críticas.
- Use sentinelas de fila por consumidor. Um "poison pill" por thread/processo worker.
- Chame
task_donepara cadaqueue.getem pipelinesthreading.Queue. Oujoinficará pendurado. - Trate exceções de Future em
result(). Futures falhos silenciosos perdem erros. - Cancele futures pendentes em caso de timeout. Não vaze trabalho em segundo plano.
E - Ponte Assíncrona
- Nunca chame I/O bloqueante dentro de corrotinas sem um executor. Bloqueia todas as tarefas.
- Compartilhe um loop por thread. Não chame
asyncio.runrepetidamente em servidores ativos, use lifespan. - Propague
contextvarspara workers de thread quando necessário. IDs de requisição para logs. - Teste código concorrente sob estresse. Testes baseados apenas em tempo são instáveis; use barreiras/eventos.
- Revise o modelo de concorrência quando as dependências se tornarem assíncronas. Pools de threads podem se tornar desnecessários.
FAQs
Causa principal de incidentes de produção?
Cache mutável compartilhado sem locks mais workers concorrentes - use filas ou sincronização adequada.
Quantos processos?
Comece com cpu_count(), reduza se a memória por worker for grande.
Asyncio é sempre mais seguro?
Mais seguro contra corridas de dados em uma única thread, mas bloquear o loop causa interrupção total - modo de falha diferente.
Devo usar threading.local?
Para estado por requisição em servidores WSGI/threaded síncronos - não é um substituto para contextvars assíncronos adequados em ASGI.
Como depurar deadlocks?
Despeje todas as pilhas de threads (faulthandler.dump_traceback_all) durante a reprodução de um travamento em staging.
Inteiros atômicos são suficientes?
itertools e operator não corrigirão invariantes de negócios compostos - use locks ou propriedade de thread única.
Pool de processos em app async?
loop.run_in_executor(process_pool, fn) - mantenha o ciclo de vida do pool atrelado à inicialização/encerramento do app.
Legado Gevent?
Mantenha se necessário; FastAPI/asyncio novos evitam monkey-patching da stdlib.
Foco do teste de carga?
Encerramento, enfileiramento de rajadas e backpressure de consumidor lento - não apenas throughput em estado estacionário.
Implantação free-threaded?
Reavalie todas as práticas - corridas de dados em dict/list se tornam mais prováveis sem a serialização do GIL.
Relacionados
- Escolhendo um Modelo de Concorrência - escolha do modelo
- threading - detalhes de locks
- Filas e Produtor/Consumidor - pipelines
- Boas Práticas Assíncronas - regras específicas de async
Versões de Stack: 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+.