Aula 13 - Concorrência Segura 🧵
Objetivo
Objetivo: Entender como o Rust garante a segurança em ambientes multi-thread, eliminando as temidas "corridas de dados" (data races) em tempo de compilação.
1. Concorrência Sem Medo 🛡️
O Rust utiliza seu sistema de Ownership e Tipagem para garantir que a memória seja acessada de forma segura por múltiplas threads.
Criando Threads
use std::thread;
use std::time::Duration;
fn main() {
thread::spawn(|| {
for i in 1..10 {
println!("Oi da thread spawned: {}", i);
thread::sleep(Duration::from_millis(1));
}
});
println!("Oi da thread principal!");
}
2. Passagem de Mensagens: Channels 📡
O Rust segue o lema: "Não comunique compartilhando memória; compartilhe memória comunicando". Os Channels (Canais) são a ferramenta para isso.
use std::sync::mpsc; // multiple producer, single consumer
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let val = String::from("oi");
tx.send(val).unwrap();
});
let recebido = rx.recv().unwrap();
println!("Recebido: {}", recebido);
}
3. Estado Compartilhado: Mutex e Arc 🔒
Quando você precisa compartilhar memória, o Rust exige o uso de ferramentas de sincronização.
- Mutex
: Garante que apenas uma thread acesse os dados por vez. - Arc
: Um ponteiro com contagem de referências atômicas, permitindo múltiplos donos em threads diferentes.
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let contador = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let contador = Arc::clone(&contador);
let handle = thread::spawn(move || {
let mut num = contador.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles { handle.join().unwrap(); }
println!("Resultado: {}", *contador.lock().unwrap());
}
4. Send e Sync: O Segredo das Traits 🧬
- Send: Permite que a posse do tipo seja transferida entre threads.
- Sync: Permite que o tipo seja acessado por múltiplas threads através de referências.
A maioria dos tipos básicos do Rust são Send e Sync. O compilador impede que você passe tipos inseguros para outras threads!
5. Visualização: Modelo de Comunicação 🔄
graph LR
T1[Thread 1] -- manda mensagem --> Canal((Canal / mpsc))
T2[Thread 2] -- manda mensagem --> Canal
Canal -- entrega --> Main[Thread Principal]
style Canal fill:#f9f,stroke:#333
6. Mini-Projeto: Processador de Imagens Multi-thread 🖼️
Crie uma simulação de processamento:
1. Um vetor de "IDs de imagens" (pode ser apenas números).
2. Use Arc e Mutex para manter uma lista de "Imagens Processadas".
3. Crie 4 threads que "processam" as imagens (use thread::sleep para simular trabalho).
4. Use um Channel para que cada thread avise a thread principal quando terminar uma imagem.
5. A thread principal deve exibir o progresso em tempo real.
7. Exercício de Fixação 🧠
- Por que o método
mpsc::channelse chama "mpsc" (Multiple Producer, Single Consumer)? - O que o método
lock()de um Mutex faz e o que acontece se outra thread já tiver o lock? - Explique a diferença entre
ArceRc(da aula de ponteiros inteligentes, se vista, ou pesquise a diferença atômica).
Próxima Aula: Vamos aplicar tudo em um Projeto CLI Profissional! 💻