Concurrencia y Multihilo en Rust: Técnicas Avanzadas

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Concurrencia y Multihilo en Rust: De lo Básico a las Técnicas Avanzadas



Rust se ha ganado la reputación de ser uno de los lenguajes de programación de sistemas más seguros, en gran parte gracias a su modelo de propiedad (ownership) y su sistema de tipos que previene las condiciones de carrera en tiempo de compilación. Sin embargo, la concurrencia no es solo seguridad, sino también eficiencia. En este artículo, exploraremos aspectos avanzados del trabajo con concurrencia y multihilo en Rust: desde primitivas básicas de sincronización hasta runtimes asíncronos modernos. El material está dirigido a desarrolladores que ya están familiarizados con los fundamentos del lenguaje.



1. Multihilo Seguro: Send, Sync y Arc<Mutex<T>>



La base del multihilo seguro en Rust son dos traits clave: Send y Sync. Un tipo que implementa Send se puede transferir de forma segura entre hilos. Un tipo que implementa Sync se puede compartir de forma segura entre hilos por referencia. La mayoría de los tipos estándar (por ejemplo, i32, String) implementan ambos traits automáticamente si solo contienen datos Send/Sync.



Para el acceso compartido a datos mutables desde múltiples hilos, se utiliza a menudo el patrón Arc<Mutex<T>>. Arc (contador de referencias atómico) permite compartir la propiedad entre hilos, y Mutex proporciona exclusión mutua.



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!("Result: {}", *counter.lock().unwrap());}


Es importante recordar: Mutex en Rust no es solo un bloqueo, sino un contenedor que protege los datos. Adquirir el bloqueo devuelve un puntero inteligente MutexGuard, que libera automáticamente el bloqueo al salir del ámbito. Esto elimina el error clásico de "olvidé desbloquear".



Consejo Avanzado: RwLock en lugar de Mutex



Si tus datos se leen con frecuencia pero se modifican raramente, usa RwLock. Permite que múltiples hilos lean los datos simultáneamente, pero bloquea la escritura mientras haya al menos un lector. Esto puede mejorar significativamente el rendimiento en escenarios de "más lectores que escritores".



use std::sync::RwLock;

let data = RwLock::new(vec![1, 2, 3]);

// Lectura — múltiples hilos pueden leer simultáneamentelet read_guard = data.read().unwrap();println!("First element: {}", read_guard[0]);drop(read_guard); // Liberamos explícitamente el bloqueo

// Escritura — acceso exclusivolet mut write_guard = data.write().unwrap();write_guard.push(4);


2. Canales (Channels): Paso de Mensajes entre Hilos



Los canales son una alternativa a la memoria compartida. En Rust hay dos tipos principales de canales: std::sync::mpsc (Multi-Productor, Unico-Consumidor) y crossbeam-channel (Multi-Productor, Multi-Consumidor).



El mpsc estándar es ideal para escenarios de "un consumidor, muchos productores". Funciona como una cola: los remitentes colocan mensajes y el receptor los recoge en orden FIFO.



use std::sync::mpsc;use std::thread;

fn main() { let (tx, rx) = mpsc::channel();

let tx1 = tx.clone(); thread::spawn(move || { tx1.send("Mensaje desde el hilo 1").unwrap(); });

thread::spawn(move || { tx.send("Mensaje desde el hilo 2").unwrap(); });

for received in rx { println!("Recibido: {}", received); }}


Blogs

Book Recommendations