<h2>Criando Threads em Rust</h2>
<p>A criação de threads em Rust é simples e segura graças ao sistema de tipos da linguagem. A função <code>std::thread::spawn()</code> permite iniciar uma nova thread, retornando um identificador (<code>JoinHandle</code>) que você pode usar para aguardar sua conclusão. Diferentemente de linguagens como C ou Java, Rust força você a pensar sobre propriedade e ciclo de vida do código que executa em paralelo, eliminando dados races em tempo de compilação.</p>
<pre><code class="language-rust">use std::thread;
use std::time::Duration;
fn main() {
let handle = thread::spawn(|| {
for i in 1..=5 {
println!("Thread: {}", i);
thread::sleep(Duration::from_millis(100));
}
});
for i in 1..=3 {
println!("Main: {}", i);
thread::sleep(Duration::from_millis(150));
}
handle.join().unwrap(); // Aguarda conclusão
println!("Threads finalizadas!");
}</code></pre>
<p>Neste exemplo, a closure captura o ambiente implicitamente. O <code>join()</code> é essencial — ele bloqueia a thread principal até que a spawned thread termine. Sem ele, o programa poderia encerrar antes da thread completar seu trabalho.</p>
<h2>Sincronização com Mutex e Arc</h2>
<p>Quando múltiplas threads precisam acessar dados compartilhados, você deve usar sincronização. O <code>Mutex<T></code> protege dados contra acesso simultâneo, enquanto <code>Arc<T></code> (Atomic Reference Counting) permite propriedade compartilhada entre threads. Essa combinação, <code>Arc<Mutex<T>></code>, é o padrão fundamental para dados mutáveis compartilhados.</p>
<pre><code class="language-rust">use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..5 {
let counter_clone = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter_clone.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Resultado: {}", *counter.lock().unwrap());
}</code></pre>
<p>O <code>Arc::clone()</code> incrementa o contador de referências sem clonar os dados reais. O <code>lock()</code> tenta adquirir um lock exclusivo; se outra thread já possui, bloqueia até liberação. O <code>unwrap()</code> panic se ocorrer poison (quando uma thread morre enquanto holds o lock), mas para código robusto, você deveria tratar esse cenário. Este código imprime <code>Resultado: 5</code>, demonstrando sincronização correta.</p>
<h2>Channels para Comunicação entre Threads</h2>
<p>Para padrões produtor-consumidor, <code>mpsc::channel()</code> (multiple producer, single consumer) oferece uma forma elegante de comunicação sem locks explícitos. O sender envia mensagens; o receiver as consome. Esse padrão evita compartilhamento de estado mutável, alinhado com princípios de concorrência segura.</p>
<pre><code class="language-rust">use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
let handle = thread::spawn(move || {
let valores = vec![1, 2, 3, 4, 5];
for val in valores {
tx.send(val).unwrap();
thread::sleep(std::time::Duration::from_millis(100));
}
});
for received in rx {
println!("Recebido: {}", received);
}
handle.join().unwrap();
}</code></pre>
<p>Aqui, o sender (<code>tx</code>) é movido para a thread. Cada valor é enviado e o loop receptor itera sobre mensagens até o sender ser dropado (indicando fim da comunicação). Channels são preferíveis a Mutex para cenários de comunicação porque são mais expressivos e evitam deadlocks comuns. O <code>unwrap()</code> no <code>send()</code> panic se o receiver foi dropado; versões robustas verificam <code>Result</code>.</p>
<h2>Padrões Avançados e Boas Práticas</h2>
<p>Para aplicações complexas, considere thread pools com bibliotecas como <code>rayon</code> ou <code>tokio</code>. O <code>rayon</code> simplifica paralelismo de dados (map-reduce), enquanto <code>tokio</code> oferece async/await para I/O-bound tasks, usando menos threads que código blocking. Para deadlock prevention, sempre adquira locks na mesma ordem globalmente e use timeouts quando apropriado.</p>
<pre><code class="language-rust">use rayon::prelude::*;
fn main() {
let data = vec![1, 2, 3, 4, 5, 6, 7, 8];
let result: Vec<i32> = data
.par_iter()
.map(|x| x * x)
.collect();
println!("{:?}", result); // [1, 4, 9, 16, 25, 36, 49, 64]
}</code></pre>
<p>Este exemplo com <code>rayon</code> distribui o trabalho entre threads automaticamente — não há spawn manual. É ideal para CPU-bound tasks. Rust força você a pensar sobre segurança, mas oferece ferramentas poderosas para explorar paralelismo sem sacrificar confiabilidade.</p>
<h2>Conclusão</h2>
<p>Rust torna concorrência segura através de três mecanismos principais: <strong>propriedade e ciclo de vida</strong> previnem data races em compilação; <strong>Mutex + Arc</strong> sincronizam dados compartilhados com segurança; <strong>channels</strong> comunicam entre threads eliminando estado mutável compartilhado. O sistema de tipos força boas práticas — código que compila é seguro para dados races. Domine <code>spawn()</code>, <code>join()</code>, <code>Mutex</code>, <code>Arc</code> e <code>mpsc::channel()</code> e você terá fundação sólida para qualquer aplicação concorrente.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://doc.rust-lang.org/book/ch16-00-concurrency.html" target="_blank" rel="noopener noreferrer">The Rust Book - Fearless Concurrency</a></li>
<li><a href="https://doc.rust-lang.org/std/sync/" target="_blank" rel="noopener noreferrer">Rust std::sync documentation</a></li>
<li><a href="https://docs.rs/rayon/" target="_blank" rel="noopener noreferrer">Rayon: Data Parallelism</a></li>
<li><a href="https://tokio.rs/" target="_blank" rel="noopener noreferrer">Tokio async runtime</a></li>
<li><a href="https://www.oreilly.com/library/view/programming-rust-2nd/9781492052586/" target="_blank" rel="noopener noreferrer">Programming Rust, 2nd Edition - Jim Blandy, Jason Orendorff</a></li>
</ul>