Arc para Estado Compartilhado
Arc (atomically reference counted) permite que múltiplas threads compartilhem a propriedade da mesma alocação no heap. Combine-o com Mutex, RwLock ou atômicos quando as threads precisarem do mesmo estado.
Receita
Cartão de referência rápida - pronto para copiar e colar.
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let shared = Arc::new(Mutex::new(0));
let handle = thread::spawn({
let shared = Arc::clone(&shared);
move || {
*shared.lock().unwrap() += 1;
}
});
handle.join().unwrap();
}Quando usar isso: Múltiplas threads precisam de acesso de longa duração à mesma configuração, cache ou contador.
Exemplo de Trabalho
use std::sync::{Arc, RwLock};
use std::thread;
struct Config {
api_url: String,
timeout_ms: u64,
}
fn main() {
let config = Arc::new(RwLock::new(Config {
api_url: "https://api.example.com".into(),
timeout_ms: 5000,
}));
let reader = {
let config = Arc::clone(&config);
thread::spawn(move || {
let cfg = config.read().unwrap();
println!("timeout = {}", cfg.timeout_ms);
})
};
let writer = {
let config = Arc::clone(&config);
thread::spawn(move || {
let mut cfg = config.write().unwrap();
cfg.timeout_ms = 3000;
})
};
reader.join().unwrap();
writer.join().unwrap();
}O que isso demonstra:
Arc::cloneincrementa a contagem de referências, não faz clone profundo dos dados.RwLockprotege oConfiginterno para leitores e escritores.- Cada thread obtém seu próprio handle
Arcviamove.
Mergulho Profundo
Como Funciona
- Contagem de referências:
Arc::cloneé barato (incremento atômico). Os dados são liberados quando o últimoArcé descartado. - Imutabilidade do próprio Arc: Você não pode obter
&mut TdeArc<T>sem mutabilidade interior (Mutex,RwLock, atômicos). - Segurança de thread:
ArcéSend + SyncquandoT: Send + Sync. - Referências fracas:
Arc::downgradecriaWeakpara quebrar ciclos (callbacks, grafos).
Composições Comuns
| Padrão | Caso de uso |
|---|---|
Arc<Mutex<T>> | Struct mutável compartilhada |
Arc<RwLock<T>> | Dados compartilhados com muitas leituras |
Arc<T> (imutável) | Configuração compartilhada após inicialização |
Arc<AtomicUsize> | Contador compartilhado simples |
Notas de Rust
// Clone barato - prefira Arc::clone(&x) em vez de x.clone() para clareza
let a = Arc::new(vec![1, 2, 3]);
let b = Arc::clone(&a);
// Weak quebra ciclos de Arc
use std::sync::Weak;
let weak: Weak<Vec<i32>> = Arc::downgrade(&a);- Não confunda
Arc::clonecom clonar os dados internos. - Para compartilhamento em uma única thread, use
Rc(mais barato, nãoSend). - Em código assíncrono,
Arcé o padrão para estado compartilhado da aplicação em manipuladores Axum.
Armadilhas
- Clone profundo por engano -
(*arc).clone()clona oTinterno, não oArc. Correção: UseArc::clone(&arc)para outro handle. - Arc sem mutabilidade interior - Não é possível mutar apenas através de
Arc<T>. Correção: AdicioneMutex,RwLock, ou o padrão rebuild-and-swap. - Lock mantido em Arc entre await - Tarefas assíncronas precisam de
Send; guards de mutex não são. Correção: Usetokio::sync::Mutexe blocos escopados. - Vazamentos de memória via ciclos de Arc - O pai mantém
Arc<Filho>e o filho mantémArc<Pai>. Correção: UseWeakem um dos lados. - Estado compartilhado demais - Tudo em um único
Arc<Mutex<App>>serializa o mundo. Correção: Particione o estado ou use atores/canais.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
Rc | Compartilhamento em thread única | Dados cruzam limites de threads |
| Canais | Papéis claros de produtor/consumidor | Muitos leitores precisam do snapshot mais recente |
thread_local! | Caches por thread | Consistência global necessária |
| Banco de dados / armazenamento externo | Durabilidade e múltiplos processos | Apenas em memória, sensível à latência |
FAQs
Arc vs Rc?
Arc usa contagem atômica de referências (Send + Sync). Rc é para thread única e mais rápido quando threads não estão envolvidas.
Arc::clone é caro?
É um incremento atômico - barato em comparação com clonar dados internos grandes, mas evite clonar em loops apertados se a análise de desempenho mostrar gargalos.
Posso obter &mut de Arc?
Não diretamente. Use mutabilidade interior ou Arc::get_mut apenas quando você tiver a única referência forte restante.
Como Axum usa Arc?
Arc<AppState> é clonado por invocação de manipulador - um incremento barato de ponteiro para pools e configurações compartilhadas.
Referências fracas?
Weak não mantém os dados vivos. Faça o upgrade com weak.upgrade() para um Arc temporário quando o objeto ainda existir.
Arc e ordem de Drop?
O último Arc descartado executa o Drop no T interno. A ordem entre threads é não determinística, a menos que você sincronize o desligamento.
Arc pode conter um Mutex?
Sim - Arc<Mutex<T>> é o padrão mutável compartilhado para threads e muitos serviços de sincronização.
Copy vs Arc?
Arc é para dados compartilhados alocados no heap. Tipos Copy como i32 não precisam de Arc, a menos que estejam encapsulados em uma struct compartilhada maior.
Dica de performance?
Reduza o escopo do lock, particione instâncias de Arc por partição, ou use snapshots imutáveis de Arc para caminhos com muitas leituras.
Testando código Arc?
Use Arc::strong_count em testes para verificar se os handles são descartados, ou injete traits para simular serviços compartilhados.
Relacionado
- Mutex & RwLock - mutabilidade interior
- Send & Sync - limites de thread do Arc
- Canais - alternativa ao estado Arc compartilhado
- Estado Compartilhado em Async - Arc em Tokio
- Melhores Práticas de Concorrência - compartilhe com cuidado
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+.