Volver al inicio

Arc y Mutex para estado paralelo en Rust

El artículo cubre el uso de Arc y Mutex para acceso seguro al estado compartido en aplicaciones Rust multihilo. Incluye un ejemplo de contador, comparación de punteros inteligentes y recomendaciones de optimización. Adecuado para desarrolladores intermedios/senior.

Estado paralelo en Rust: Arc + Mutex en la práctica
Advertisement 728x90

Acceso Seguro a Estado Compartido en Aplicaciones Rust Multihilo

El uso de memoria compartida sin protección entre hilos conduce a condiciones de carrera, donde lecturas y escrituras concurrentes corrompen los datos. Incluso con sincronización, pueden ocurrir bloqueos mutuos que ralentizan la ejecución. Depurar este tipo de código es complicado, aunque Rust previene muchos errores en tiempo de compilación.

En sistemas de alta carga como contadores o cachés, el acceso compartido está justificado cuando se utilizan primitivas de sincronización adecuadas.

Arc y Mutex para Compartir Datos de Forma Segura entre Hilos

Para transferir datos de forma segura entre hilos, combina Arc<T> y Mutex<T>. Arc<T> proporciona conteo de referencias atómico con soporte Send y Sync, permitiendo múltiples propietarios en un entorno multihilo. Mutex<T> garantiza exclusión mutua: solo un hilo accede a los datos a la vez.

Google AdInline article slot

Arc se clona para cada hilo usando Arc::clone, y el acceso se obtiene mediante lock(), que devuelve un MutexGuard. El método lock() puede entrar en pánico con un mutex envenenado, así que en producción, maneja los errores en lugar de usar unwrap.

Ejemplo de Implementación de un Contador

use std::thread;
use std::time::Duration;
use std::sync::{Arc, Mutex};

struct Counter {
    value: i64
}

fn main() {
    let counter = Arc::new(Mutex::new(Counter {value: 0}));

    let increment = Arc::clone(&counter);
    let handler1 = thread::spawn(move || {
        for _ in 0..10 {
            let mut guard = increment.lock().unwrap();
            guard.value += 1;
            println!("inc: {}", guard.value);
            thread::sleep(Duration::from_millis(100));
        }
    });

    let reader = Arc::clone(&counter);
    let handler2 = thread::spawn(move || {
        for _ in 0..15 {
            let guard = reader.lock().unwrap();
            println!("read: {}", guard.value);
            thread::sleep(Duration::from_millis(180));
        }
    });

    handler1.join().unwrap();
    handler2.join().unwrap();

    let final_guard = counter.lock().unwrap();
    println!("final: {}", final_guard.value);
}

Este código demuestra el incremento en un hilo y la lectura en otro. El valor final siempre es 10, sin condiciones de carrera.

Comparación de Punteros Inteligentes Clave

Rust ofrece varios punteros inteligentes para la gestión de memoria. La elección depende de los requisitos de seguridad de hilos y propiedad:

Google AdInline article slot
  • Box<T>: Propiedad única en el montón, adecuado para tipos de tamaño desconocido, no es Send + Sync.
  • Rc<T>: Propiedad múltiple dentro de un solo hilo, no es seguro para hilos.
  • Arc<T>: Propiedad múltiple entre hilos, Send + Sync, con sobrecarga por operaciones atómicas.
  • RefCell<T>: Mutabilidad interior con verificaciones de préstamo en tiempo de ejecución, para un solo hilo.
  • Cell<T>: Mutabilidad ligera para tipos Copy, sin pánicos.
  • Mutex<T>: Bloqueo para acceso exclusivo, riesgo de bloqueos mutuos.
  • RwLock<T>: Optimización para escenarios con muchas lecturas.
  • Weak<T>: Referencias débiles para romper ciclos en Rc/Arc.
  • ManuallyDrop<T>: Control manual del destructor, inseguro.

Arc<T> es único en la biblioteca estándar para propiedad múltiple segura entre hilos.

Alternativas a Mutex para Optimización

En escenarios con lecturas frecuentes, considera RwLock<T>: múltiples lectores pueden proceder en paralelo, los escritores son exclusivos. El rendimiento es mayor cuando la proporción lector/escritor > 1.

Para acceso sin bloqueos, usa atómicos (std::sync::atomic) si los datos son primitivos como AtomicI64. Evitan bloqueos por completo.

Google AdInline article slot

Conclusiones Clave

  • El acceso compartido solo está justificado en sistemas de alta carga con datos compartidos; de lo contrario, prefiere la transferencia de propiedad.
  • Siempre combina Arc<T> con Mutex<T> o RwLock<T> para seguridad entre hilos.
  • Evita unwrap() en lock(): maneja PoisonError.
  • Arc<T> tiene sobrecarga por operaciones atómicas—mide en benchmarks.
  • Para casos simples, usa canales (mpsc) en lugar de estado compartido.

— Editorial Team

Advertisement 728x90

Leer después