36 - Concurrencia sin miedo en Rust

36 - Concurrencia sin miedo en Rust

¡Bienvenidos de nuevo a Rustaceo.es! Hoy exploraremos uno de los aspectos más potentes y seguros de Rust: la concurrencia sin miedo. Gracias a su modelo de propiedad y sistema de tipos, Rust nos permite escribir código concurrente sin los problemas comunes de los bloqueos y condiciones de carrera.


🚀 ¿Por qué la concurrencia es difícil? #

En muchos lenguajes, manejar múltiples hilos de ejecución es complicado debido a:

  • Condiciones de carrera: Acceso simultáneo a datos compartidos sin control adecuado.
  • Interbloqueos (deadlocks): Dos o más hilos esperando recursos que nunca se liberan.
  • Datos corruptos: Escritura concurrente sin sincronización adecuada.

Rust soluciona estos problemas con su modelo de propiedad, garantizando seguridad en tiempo de compilación.


🔀 Opciones para la concurrencia en Rust #

Rust nos ofrece varias herramientas para manejar concurrencia de manera segura:

  1. Threads (std::thread): Creación de hilos de ejecución.
  2. Mensajería con mpsc: Comunicación entre hilos con canales.
  3. Mutexes y Arc: Sincronización de datos compartidos.
  4. async/await: Programación concurrente asincrónica.

🧵 Creando threads en Rust #

Podemos iniciar múltiples hilos fácilmente usando std::thread::spawn:

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

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..5 {
            println!("Hilo secundario: {}", i);
            thread::sleep(Duration::from_millis(500));
        }
    });

    for i in 1..3 {
        println!("Hilo principal: {}", i);
        thread::sleep(Duration::from_millis(500));
    }

    handle.join().unwrap();
}

Seguro y sin bloqueos inesperados: join() asegura que el hilo termine antes de continuar.


📬 Comunicación segura entre hilos con mpsc #

El modelo “pasar mensajes” en Rust usa mpsc::channel para comunicación segura entre hilos.

use std::sync::mpsc;
use std::thread;
use std::time::Duration;

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let mensaje = String::from("¡Hola desde otro hilo!");
        tx.send(mensaje).unwrap();
    });
    
    let recibido = rx.recv().unwrap();
    println!("Mensaje recibido: {}", recibido);
}

Evita datos compartidos: mpsc::channel permite enviar datos sin compartir memoria mutable.


🔒 Sincronización con arc y mutex #

Cuando necesitamos compartir datos entre hilos, usamos Arc<Mutex<T>> para mantener seguridad.

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

fn main() {
    let contador = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..5 {
        let contador = Arc::clone(&contador);
        let handle = thread::spawn(move || {
            let mut num = contador.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Valor final del contador: {}", *contador.lock().unwrap());
}

Arc (Atomic Reference Counter): Permite múltiples propietarios del Mutex sin violar reglas de propiedad. ✔ Mutex (Mutual Exclusion): Evita condiciones de carrera en datos compartidos.


⚡ Programación asincrónica con async/await #

Rust ofrece un modelo asincrónico y eficiente con async y tokio para manejar múltiples tareas sin bloqueo.

use tokio::time::{sleep, Duration};

#[Tokio::main]
async fn main() {
    let tarea1 = tokio::spawn(async {
        sleep(Duration::from_secs(2)).await;
        println!("Tarea 1 completada");
    });

    let tarea2 = tokio::spawn(async {
        println!("Tarea 2 iniciada");
    });

    tarea1.await.unwrap();
    tarea2.await.unwrap();
}

async fn y await: Permiten ejecutar múltiples tareas sin bloquear el hilo principal. ✔ tokio::spawn: Lanza tareas concurrentes dentro de un runtime asincrónico.


🏆 Conclusión #

Rust nos ofrece herramientas poderosas para la concurrencia sin miedo, evitando errores comunes en otros lenguajes.

Threads (std::thread): Fácil manejo de múltiples hilos. ✔ Mensajería con mpsc: Comunicación eficiente sin memoria compartida. ✔ Arc<Mutex<T>>: Sincronización segura para datos compartidos. ✔ async/await: Eficiencia sin bloqueos con Tokio.

🔮 Próximo paso: Explora proyectos más avanzados con concurrencia en Rust, como servidores web y procesamiento paralelo.

¡Hasta la próxima, Rustaceos!