Boas Práticas de Concorrência
Regras para concorrência segura e de fácil manutenção em Rust - prefira passagem de mensagens, minimize o escopo de locks e ajuste o modelo de paralelismo à carga de trabalho.
Como Usar Esta Lista
- Aplique antes de adicionar
Arc<Mutex<Everything>>a um serviço. - Use como um checklist de PR para mudanças de spawning de threads e estado compartilhado.
- Revise após incidentes (deadlocks, contagens incorretas, encerramento travado).
A - Seleção de Modelo
- Faça o profiling antes de escolher threads vs Rayon vs async. Rótulos enganam sem dados de tempo.
- Prefira passagem de mensagens em vez de mutação compartilhada. Canais codificam a transferência de propriedade claramente.
- Use Rayon para maps/reduces de paralelismo de dados limitados por CPU. Não para I/O bloqueante dentro de closures.
- Reserve threads do SO para paralelismo real ou isolamento de bloqueio. Milhares de threads são um mau sinal.
- Documente o modelo de concorrência no README do módulo ou ADR. Leitores futuros precisam do porquê.
B - Locks & Estado Compartilhado
- Mantenha as seções críticas curtas. Clone os dados antes de I/O lento ou chamadas de sistema.
- Adquira locks em uma ordem global fixa. Documente a ordem para prevenir deadlocks.
- Prefira
RwLockapenas para dados com muitas leituras. Escritores dominam -> useMutexou canais. - Use atômicos para contadores simples e flags de shutdown. Emparelhe
Release/Acquireao publicar estado. - Evite objetos "god"
Arc<Mutex<LargeAppState>>. Particione o estado ou use tarefas de ator.
C - Threads & Lifetimes
- Use
thread::scopeao emprestar dados da stack. Evite clones desnecessários deArcpara dividir e conquistar. - Junte ou desanexe intencionalmente. Threads desanexadas não devem tocar em recursos pais após a saída.
- Nomeie threads para observabilidade. Aparece em logs, traces e dumps de crash.
- Trate
JoinErrore envenenamento de mutex. Não useunwrapsilenciosamente em caminhos de produção. - Limite a contagem de workers aos orçamentos de CPU, memória e FD. Matemática, não apenas padrões.
D - Canais & Shutdown
- Prefira canais com limite sob carga. Filas ilimitadas escondem backpressure até OOM.
- Descarte todos os remetentes ou envie um sentinela de shutdown. Recebedores não devem bloquear para sempre.
- Use
recv_timeoutem testes e shutdown. Falhe ruidosamente em vez de travar o CI. - Envie
Resultpor item em pipelines. Falha parcial é melhor que pânico de tudo ou nada. - Separe o plano de controle dos canais do plano de dados. Sinais de parada vs mensagens de trabalho.
E - Limite Async
- Não bloqueie o executor async em
mutex.lock()ourecv(). Use primitivastokio::syncem código async. - Descarregue trabalho de CPU com
spawn_blockingou Rayon a partir do async. Mantenha a latência previsível. - Nunca mantenha
std::sync::MutexGuardatravés de.await. QuebraSende atrasa workers. - Reavalie pools de threads quando bibliotecas se tornam async. Menos pontes, topologia mais simples.
- Teste de carga de shutdown gracioso. Corridas aparecem na implantação e SIGTERM.
FAQs
Causa principal de incidentes de produção?
Cache mutável compartilhado sem ordem de lock clara mais escritores concorrentes - prefira canais ou locks particionados.
Quantas threads?
Comece perto dos núcleos da CPU para trabalho de CPU; para servidores síncronos com I/O pesado, dimensione para conexão e limites de pool, não num_cpus * 10 cegamente.
Mutex ou canais?
Se cada atualização for um evento independente, canais. Se muitos leitores precisarem da mesma struct ao vivo, Arc<RwLock<T>>.
Quando o estado compartilhado é aceitável?
Quando os invariantes são simples, o escopo do lock é minúsculo e a contenção é baixa - contadores, snapshots de configuração, caches com muitas leituras.
Testando código concorrente?
Testes de estresse, injeção de sleeps, execução de testes multi-threaded, considere loom para algoritmos lock-free.
Rayon em bibliotecas?
Exponha a API sequencial por padrão; caminho paralelo opcional atrás de um flag de feature se os consumidores puderem ser single-threaded.
Estáticos globais?
OnceLock, atômicos, ou lazy_static/std::sync::OnceLock - evite static mut não sincronizado.
FFI e threads?
Documente qual thread chama FFI; Send/Sync em handles deve corresponder às regras da biblioteca C.
Observabilidade?
Nomes de threads, spans de tracing por worker, métricas de profundidade de fila e tempo de espera de lock.
Primeiro passo de refatoração?
Substitua a mutação compartilhada por uma única thread proprietária e comandos baseados em canais.
Relacionados
- Noções Básicas de Concorrência - começando
- Canais - passagem de mensagens
- Deadlocks & Condições de Corrida - modos de falha
- Boas Práticas Async - regras específicas de async
- Boas Práticas do Tokio - ajuste do runtime
Versões da Stack: Esta página foi escrita para Rust 1.97.0 (edição 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, e Polars 0.46+.