Concurrencia y Multihilo en Rust: de la A a la Z

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 la A a la Z



Las aplicaciones modernas requieren cada vez más un uso eficiente de los procesadores multinúcleo. Rust ofrece un enfoque único para la concurrencia y el multihilo, combinando alto rendimiento con garantías de seguridad de memoria en tiempo de compilación. En este artículo, analizaremos los conceptos clave: desde la creación de hilos hasta el uso de mutexes y canales, con ejemplos prácticos de código.



1. Fundamentos del multihilo: creación y gestión de hilos



En Rust, los hilos se crean con la biblioteca estándar std::thread. Cada hilo se ejecuta de forma independiente, pero puede intercambiar datos con otros hilos.



Ejemplo simple de creación de un hilo:



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

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

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

handle.join().unwrap(); // Esperamos a que termine el hilo}


Puntos clave:

  • thread::spawn acepta un closure que se ejecuta en un nuevo hilo.
  • El método join bloquea el hilo actual hasta que el hilo especificado finalice.
  • Sin join, el hilo principal podría finalizar antes que el hilo secundario.


Para transferir datos entre hilos, use closures con move:



use std::thread;

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

let handle = thread::spawn(move || { println!("Datos desde el hilo: {:?}", data); });

handle.join().unwrap(); // println!("{:?}", data); // ¡Error! data ya fue movido}


La palabra clave move transfiere la propiedad de los datos al closure, evitando condiciones de carrera en tiempo de compilación.



2. Intercambio seguro de datos: mutexes y bloqueos



Cuando varios hilos acceden a datos compartidos, existe el riesgo de condiciones de carrera. Rust resuelve este problema con mutexes (Mutex) y tipos atómicos.



Ejemplo de uso de Mutex con Arc (contador de referencias 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}


Detalles importantes:

  • Arc<T> permite que varios hilos posean los mismos datos (clonación atómica).
  • Mutex::lock() devuelve un bloqueo; si el bloqueo ya está ocupado, el hilo espera.
  • El bloqueo se libera automáticamente al salir del ámbito.


Para un control más preciso, use RwLock, que separa lectores y escritores:



use std::sync::RwLock;

let lock = RwLock::new(5);

// Lectura: varios hilos simultáneamente{ let r1 = lock.read().unwrap(); let r2 = lock.read().unwrap(); println!("Leyendo: {} y {}", r1, r2);}

// Escritura: solo un hilo{ let mut w = lock.write().unwrap(); *w += 1; println!("Escribiendo: {}", w);}


3. Canales para transmisión de mensajes



Una alternativa a la memoria compartida es el modelo de actores mediante canales. Rust proporciona un canal multiproductor y un solo consumidor (mpsc).



Ejemplo básico:



u                

Blogs

Book Recommendations