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 unMutex
y envuelto en unArc
para permitir el acceso compartido entre hilos. - Clonación de Arc:
Arc::clone(&counter)
clona elArc
para que cada hilo tenga su propia referencia al contador. - Bloqueo del Mutex:
counter.lock().unwrap()
bloquea elMutex
para acceder al contador.unwrap()
se usa para manejar posibles errores de bloqueo. - Incremento del Contador:
*num += 1
incrementa 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
Arc
para cada hilo puede resultar en errores de compilación. - Desbloqueo Automático: El
Mutex
se desbloquea automáticamente cuando el guardián (num
en este caso) sale del alcance. - Condiciones de Carrera: Asegúrate de usar
Mutex
correctamente 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