El modelo de ownership de Rust garantiza en tiempo de compilación que no haya data races: dos hilos no pueden leer y escribir el mismo dato simultáneamente sin sincronización. Pero escribir código concurrente correcto requiere entender las herramientas disponibles: Mutex para acceso exclusivo, RwLock para lecturas paralelas, canales mpsc para comunicación por mensajes y Barrier para sincronización por fases.
Arc<Mutex<T>>: estado mutable compartido
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let contador = Arc::new(Mutex::new(0u64));
let mut handles = vec![];
for _ in 0..8 {
let c = Arc::clone(&contador);
handles.push(thread::spawn(move || {
for _ in 0..1000 {
let mut guard = c.lock().unwrap();
*guard += 1;
} // guard se libera automáticamente al salir de scope
}));
}
for h in handles { h.join().unwrap(); }
println!("Total: {}", *contador.lock().unwrap()); // 8000
}
RwLock: múltiples lectores, un escritor
use std::sync::{Arc, RwLock};
use std::thread;
fn main() {
let cache = Arc::new(RwLock::new(vec!["inicial".to_string()]));
let mut handles = vec![];
// 5 hilos lectores en paralelo
for i in 0..5 {
let c = Arc::clone(&cache);
handles.push(thread::spawn(move || {
let r = c.read().unwrap(); // múltiples reads simultáneos OK
println!("Lector {i}: {:?}", *r);
}));
}
// 1 hilo escritor (espera a que no haya lectores)
let c = Arc::clone(&cache);
handles.push(thread::spawn(move || {
let mut w = c.write().unwrap(); // exclusivo
w.push("nuevo elemento".to_string());
}));
for h in handles { h.join().unwrap(); }
}
Canales mpsc: comunicación por mensajes
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::channel();
// Múltiples productores
for i in 0..3 {
let tx_clon = tx.clone();
thread::spawn(move || {
thread::sleep(Duration::from_millis(i * 100));
tx_clon.send(format!("mensaje de hilo {i}")).unwrap();
});
}
drop(tx); // cerrar el transmisor original para que rx sepa cuándo acabar
// El receptor bloquea hasta recibir o que todos los tx se cierren
for msg in rx {
println!("Recibido: {msg}");
}
}
Canal síncrono con capacidad limitada
use std::sync::mpsc;
use std::thread;
fn main() {
// sync_channel: el emisor se bloquea si el buffer está lleno
let (tx, rx) = mpsc::sync_channel::(2); // buffer de 2
let h = thread::spawn(move || {
for i in 0..5 {
println!("Enviando {i}");
tx.send(i).unwrap(); // se bloquea cuando buffer lleno
}
});
thread::sleep(std::time::Duration::from_millis(100));
for _ in 0..5 {
println!("Recibido: {}", rx.recv().unwrap());
}
h.join().unwrap();
}
Barrier: sincronización por fases
use std::sync::{Arc, Barrier};
use std::thread;
fn main() {
let barrera = Arc::new(Barrier::new(4));
let mut handles = vec![];
for i in 0..4 {
let b = Arc::clone(&barrera);
handles.push(thread::spawn(move || {
println!("Hilo {i}: fase 1");
b.wait(); // todos esperan aquí hasta que 4 hilos lleguen
println!("Hilo {i}: fase 2");
b.wait();
println!("Hilo {i}: terminado");
}));
}
for h in handles { h.join().unwrap(); }
}
El error de compilación más frecuente
use std::rc::Rc;
use std::thread;
fn main() {
let datos = Rc::new(vec![1, 2, 3]);
let d = Rc::clone(&datos);
thread::spawn(move || {
println!("{:?}", d);
});
}
// error[E0277]: `Rc<Vec<i32>>` cannot be sent between threads safely
// Solución: usa Arc en lugar de Rc
Resumen
Arc<Mutex<T>>: el patrón estándar para estado mutable compartido entre hilos.RwLock: permite múltiples lectores simultáneos; un solo escritor con acceso exclusivo.mpsc::channel(): canales de capacidad ilimitada;sync_channelpara buffers acotados con back-pressure.- Clona el
Senderpara múltiples productores; cierra todos los transmisores para señalizar fin. Barriersincroniza fases: todos los hilos esperan hasta que el último llega.Rcno implementaSend; usaArcsiempre que datos crucen límites de hilo.
