En este tema, exploraremos cómo manejar el estado compartido en programas concurrentes en Rust. La concurrencia puede ser compleja debido a la necesidad de coordinar el acceso a datos compartidos entre múltiples hilos. Rust proporciona herramientas y mecanismos para manejar estos desafíos de manera segura y eficiente.
Conceptos Clave
- Mutex (Mutual Exclusion): Un mecanismo que permite que solo un hilo acceda a un recurso compartido a la vez.
- Arc (Atomic Reference Counting): Un contador de referencias atómico que permite compartir datos entre múltiples hilos de manera segura.
- Condiciones de Carrera: Situaciones donde el resultado del programa depende del orden de ejecución de los hilos.
Mutex
Un Mutex en Rust garantiza que solo un hilo pueda acceder a los datos protegidos por el Mutex a la vez. Aquí hay un ejemplo básico de cómo usar un Mutex:
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());
}Explicación del Código
- Arc:
Arc::new(Mutex::new(0))crea un contador protegido por unMutexy envuelto en unArcpara permitir el acceso compartido entre hilos. - Clonación de Arc:
Arc::clone(&counter)clona elArcpara que cada hilo tenga su propia referencia al contador. - Bloqueo del Mutex:
counter.lock().unwrap()bloquea elMutexpara acceder al contador.unwrap()se usa para manejar posibles errores de bloqueo. - Incremento del Contador:
*num += 1incrementa el contador protegido por elMutex. - Unión de Hilos:
handle.join().unwrap()asegura que todos los hilos completen su ejecución antes de continuar.
Ejercicio Práctico
Ejercicio 1: Contador Concurrente
Crea un programa que inicie 5 hilos, cada uno incrementando un contador compartido 100 veces. Usa Mutex y Arc para manejar el estado compartido.
Solución
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 = Arc::clone(&counter);
let handle = thread::spawn(move || {
for _ in 0..100 {
let mut num = counter.lock().unwrap();
*num += 1;
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Final counter value: {}", *counter.lock().unwrap());
}Explicación del Código
- Bucle de Hilos: Se crean 5 hilos, cada uno incrementando el contador 100 veces.
- Bloqueo y Desbloqueo: Cada hilo bloquea el
Mutex, incrementa el contador y luego desbloquea elMutex.
Retroalimentación y Consejos
- Errores Comunes: Olvidar clonar el
Arcpara cada hilo puede resultar en errores de compilación. - Desbloqueo Automático: El
Mutexse desbloquea automáticamente cuando el guardián (numen este caso) sale del alcance. - Condiciones de Carrera: Asegúrate de usar
Mutexcorrectamente para evitar condiciones de carrera.
Conclusión
En esta sección, hemos aprendido cómo manejar el estado compartido en programas concurrentes usando Mutex y Arc en Rust. Estos mecanismos nos permiten coordinar el acceso a datos compartidos de manera segura y eficiente, evitando condiciones de carrera y otros problemas de concurrencia. En el próximo módulo, exploraremos características avanzadas de Rust, como macros y el uso de Rust inseguro.
Curso de Programación en Rust
Módulo 1: Introducción a Rust
- ¿Qué es Rust?
- Configuración del Entorno de Rust
- Programa "Hola, Mundo!"
- Sintaxis y Estructura Básica
