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

  1. Mutex (Mutual Exclusion): Un mecanismo que permite que solo un hilo acceda a un recurso compartido a la vez.
  2. Arc (Atomic Reference Counting): Un contador de referencias atómico que permite compartir datos entre múltiples hilos de manera segura.
  3. 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

  1. Arc: Arc::new(Mutex::new(0)) crea un contador protegido por un Mutex y envuelto en un Arc para permitir el acceso compartido entre hilos.
  2. Clonación de Arc: Arc::clone(&counter) clona el Arc para que cada hilo tenga su propia referencia al contador.
  3. Bloqueo del Mutex: counter.lock().unwrap() bloquea el Mutex para acceder al contador. unwrap() se usa para manejar posibles errores de bloqueo.
  4. Incremento del Contador: *num += 1 incrementa el contador protegido por el Mutex.
  5. 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

  1. Bucle de Hilos: Se crean 5 hilos, cada uno incrementando el contador 100 veces.
  2. Bloqueo y Desbloqueo: Cada hilo bloquea el Mutex, incrementa el contador y luego desbloquea el Mutex.

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.

© Copyright 2024. Todos los derechos reservados