Concorrência e Multithreading em Rust: do A ao Z

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Concorrência e Multithreading em Rust: do A ao Z



Os aplicativos modernos exigem cada vez mais o uso eficiente de processadores multi-core. O Rust oferece uma abordagem única para concorrência e multithreading, combinando alta performance com garantias de segurança de memória em tempo de compilação. Neste artigo, abordaremos os conceitos-chave: desde a criação de threads até o uso de mutexes e canais, com exemplos práticos de código.



1. Fundamentos do multithreading: criação e gerenciamento de threads



Em Rust, as threads são criadas usando a biblioteca padrão std::thread. Cada thread é executada de forma independente, mas pode trocar dados com outras threads.



Exemplo simples de criação de uma thread:



use std::thread;use std::time::Duration;

fn main() { let handle = thread::spawn(|| { for i in 1..10 { println!("Thread: número {}", i); thread::sleep(Duration::from_millis(1)); } });

for i in 1..5 { println!("Thread principal: número {}", i); thread::sleep(Duration::from_millis(1)); }

handle.join().unwrap(); // Aguarda a conclusão da thread}


Pontos-chave:

  • thread::spawn recebe uma closure que é executada em uma nova thread.
  • O método join bloqueia a thread atual até que a thread especificada termine.
  • Sem join, a thread principal pode terminar antes da thread filha.


Para transferir dados entre threads, use closures com move:



use std::thread;

fn main() { let data = vec![1, 2, 3, 4, 5];

let handle = thread::spawn(move || { println!("Dados da thread: {:?}", data); });

handle.join().unwrap(); // println!("{:?}", data); // Erro! data já foi movido}


A palavra-chave move transfere a propriedade dos dados para a closure, prevenindo condições de corrida em tempo de compilação.



2. Troca segura de dados: mutexes e bloqueios



Quando várias threads acessam dados compartilhados, surge o risco de condição de corrida. O Rust resolve esse problema com mutexes (Mutex) e tipos atômicos.



Exemplo de uso de Mutex com Arc (contador de referência atômico):



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 counter = Arc::clone(&counter); let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); }

for handle in handles { handle.join().unwrap(); }

println!("Resultado: {}", *counter.lock().unwrap()); // 10}


Detalhes importantes:

  • Arc<T> permite que várias threads possuam os mesmos dados (clonagem atômica).
  • Mutex::lock() retorna um bloqueio; se o bloqueio já estiver ocupado, a thread espera.
  • O bloqueio é liberado automaticamente ao sair do escopo.


Para um controle mais refinado, use RwLock, que separa leitores e escritores:



use std::sync::RwLock;

let lock = RwLock::new(5);

// Leitura: várias threads simultaneamente{ let r1 = lock.read().unwrap(); let r2 = lock.read().unwrap(); println!("Lendo: {} e {}", r1, r2);}

// Escrita: apenas uma thread{ let mut w = lock.write().unwrap(); *w += 1; println!("Escrevendo: {}", w);}


3. Canais para transmissão de mensagens



Uma alternativa à memória compartilhada é o modelo de atores usando canais. O Rust fornece um canal multiprodutor, um consumidor (mpsc).



Exemplo básico:



u                

Blogs

Book Recommendations