Compartilhamento de estado com Arc/Mutex
Dados compartilhados com segurança
Compartilhamento de estado com Arc/Mutex é uma aula grátis de Learn Rust Coding no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Learn Rust Coding, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Learn Rust Coding inclui 4 aulas no total.
Por que o estado compartilhado é difícil
Às vezes, a passagem de mensagens não é suficiente, e várias threads realmente precisam ler e escrever os mesmos dados. O Rust não permite compartilhar um valor mutável entre threads sem proteção, pois isso poderia causar uma condição de corrida de dados.
As duas ferramentas combinadas são:
Arcpara propriedade compartilhada entre threads.Mutexpara mutação segura e exclusiva.
Rc não é seguro para threads
Rc fornece propriedade compartilhada, mas apenas em uma única thread. Sua contagem de referências não é sincronizada, portanto o compilador se recusa a enviá-lo entre threads. Para compartilhamento entre várias threads, você precisa de Arc (contagem atômica de referências).
Arc funciona como Rc, mas atualiza sua contagem com operações atômicas, tornando os clones seguros entre threads.
use std::sync::Arc;
fn main() {
let data = Arc::new(vec![1, 2, 3]);
let clone1 = Arc::clone(&data);
println!("original: {:?}", data);
println!("clone: {:?}", clone1);
println!("count: {}", Arc::strong_count(&data));
}Mutex fornece acesso exclusivo
Um Mutex envolve dados e garante que apenas uma thread os acesse por vez. Você chama .lock() para obter um MutexGuard, que desreferencia para o valor interno. O bloqueio é liberado automaticamente quando o protetor sai do escopo.
use std::sync::Mutex;
fn main() {
let m = Mutex::new(5);
{
let mut num = m.lock().unwrap();
*num += 10;
} // guard dropped here, lock released
println!("value = {:?}", m.lock().unwrap());
}Combinando Arc e Mutex
Para compartilhar dados mutáveis entre threads, envolva-os como Arc:
Arcpermite que várias threads tenham um identificador para os mesmos dados.Mutexpermite que cada thread os altere com segurança, uma por vez.
Faça um clone de Arc para cada thread antes de criá-las.
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let c = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut n = c.lock().unwrap();
*n += 1;
});
handle.join().unwrap();
println!("counter = {}", *counter.lock().unwrap());
}Um contador compartilhado entre várias threads
O exemplo clássico: dez threads incrementam um contador compartilhado. Cada thread mantém seu próprio clone de Arc e bloqueia o Mutex para adicionar um. Depois de unir todas as threads, o total é exatamente 10, sem nenhuma condição de corrida de dados.
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let c = Arc::clone(&counter);
handles.push(thread::spawn(move || {
let mut num = c.lock().unwrap();
*num += 1;
}));
}
for h in handles {
h.join().unwrap();
}
println!("Result: {}", *counter.lock().unwrap());
}O escopo do bloqueio é importante
O MutexGuard mantém o bloqueio até ser descartado. Mantê-lo durante uma tarefa lenta bloqueia outras threads. Mantenha as seções críticas curtas: bloqueie, altere e libere. Envolver o bloqueio em um bloco pequeno garante que ele seja liberado antes de qualquer processamento adicional.
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let log = Arc::new(Mutex::new(Vec::new()));
let mut handles = vec![];
for i in 0..3 {
let l = Arc::clone(&log);
handles.push(thread::spawn(move || {
{
let mut v = l.lock().unwrap();
v.push(i);
} // released quickly
}));
}
for h in handles { h.join().unwrap(); }
let mut result = log.lock().unwrap().clone();
result.sort();
println!("{:?}", result);
}Deadlocks: um risco real
Um deadlock ocorre quando duas threads mantêm, cada uma, um bloqueio de que a outra precisa, e ambas esperam indefinidamente. O Rust impede condições de corrida de dados, mas não deadlocks. Evite-os sempre bloqueando vários mutexes na mesma ordem e mantendo os bloqueios curtos.
Além disso, evite bloquear o mesmo Mutex duas vezes na mesma thread; o Mutex padrão não é reentrante.
Contaminação quando uma thread entra em pânico
Se uma thread entrar em pânico enquanto mantém um bloqueio, o Mutex ficará contaminado. Chamadas posteriores a .lock() retornarão Err, indicando que os dados podem estar inconsistentes. Você pode recuperar o protetor interno por meio de into_inner() no erro, caso decida que os dados ainda podem ser usados.
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let data = Arc::new(Mutex::new(0));
let d = Arc::clone(&data);
let _ = thread::spawn(move || {
let mut g = d.lock().unwrap();
*g = 7;
panic!("boom"); // poisons the mutex
}).join();
match data.lock() {
Ok(g) => println!("ok: {}", *g),
Err(poisoned) => println!("recovered: {}", *poisoned.into_inner()),
}
}RwLock para muitos leitores
Quando as leituras superam amplamente as escritas, um RwLock pode ser mais rápido que um Mutex. Ele permite muitos leitores simultâneos ou um único escritor. Use .read() para acesso compartilhado e .write() para acesso exclusivo.
use std::sync::{Arc, RwLock};
use std::thread;
fn main() {
let config = Arc::new(RwLock::new(String::from("v1")));
let reader = Arc::clone(&config);
let r = thread::spawn(move || {
let val = reader.read().unwrap();
println!("read: {}", *val);
});
r.join().unwrap();
{
let mut w = config.write().unwrap();
*w = String::from("v2");
}
println!("final: {}", *config.read().unwrap());
}Atomics para contadores simples
Para um único contador inteiro, um Mutex completo é exagero. Tipos como AtomicUsize oferecem atualizações sem bloqueio por meio de métodos como fetch_add. Envolva-os em Arc para compartilhá-los entre threads. Escolha uma Ordering; SeqCst é o padrão seguro mais simples.
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
fn main() {
let counter = Arc::new(AtomicUsize::new(0));
let mut handles = vec![];
for _ in 0..5 {
let c = Arc::clone(&counter);
handles.push(thread::spawn(move || {
c.fetch_add(1, Ordering::SeqCst);
}));
}
for h in handles { h.join().unwrap(); }
println!("count = {}", counter.load(Ordering::SeqCst));
}Escolhendo a ferramenta certa
Orientação rápida para estado compartilhado:
- Canal: transfere a propriedade, no estilo de pipeline.
- Arc<Mutex>: estrutura mutável compartilhada, com leituras e escritas combinadas.
- Arc<RwLock>: dados compartilhados com predominância de leitura.
- Atomics: contadores ou sinalizadores de uma única primitiva.
Prefira a ferramenta mais simples que se encaixe no caso; recorra a bloqueios somente quando a passagem de mensagens não representar bem o problema.
Verificação rápida
Teste sua compreensão sobre estado compartilhado.
Recapitulação
Você aprendeu a compartilhar estado com segurança entre threads:
Arcpermite a propriedade compartilhada segura entre threads;Rcnão.Mutexfornece mutação exclusiva por meio de uma proteção que desbloqueia automaticamente.Arc<Mutex<T>>é o padrão para dados mutáveis compartilhados.- Mantenha os escopos dos bloqueios curtos; tenha cuidado com deadlocks e envenenamento.
RwLocké adequado para dados com predominância de leitura; atomics são adequados para contadores simples.
Perguntas Frequentes
A aula “Compartilhamento de estado com Arc/Mutex” é grátis?
Sim — o texto completo de “Compartilhamento de estado com Arc/Mutex” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Learn Rust Coding, atualize para CoddyKit PRO. O curso de Learn Rust Coding inclui 4 aulas no total.
O que vou aprender em “Compartilhamento de estado com Arc/Mutex”?
Dados compartilhados com segurança Você pratica Learn Rust Coding com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar Learn Rust Coding?
Nenhuma experiência prévia é necessária. Learn Rust Coding no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.
Quanto tempo leva a aula “Compartilhamento de estado com Arc/Mutex”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de Learn Rust Coding?
Sim. Cada aula de Learn Rust Coding inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Canais mpsc
- Compartilhamento de estado com Arc/Mutex
- Threads com escopo
- Canais Crossbeam