40 - Resumen semana siete - concurrencia y aventuras asíncronas en Rust

40 - Resumen semana siete - concurrencia y aventuras asíncronas en Rust

¡Bienvenidos de nuevo a Rustaceo.es! En esta séptima semana nos sumergimos en los fundamentos y aplicaciones avanzadas de concurrencia y programación asíncrona en Rust. Desde la ejecución de múltiples hilos hasta el manejo eficiente de tareas asincrónicas, exploramos las herramientas que hacen de Rust un lenguaje seguro y eficiente para el desarrollo concurrente.


🚀 Principales temas cubiertos #

  1. Manejo de Threads en Rust: Creación y sincronización de hilos con std::thread.
  2. Comunicación entre Hilos: Uso de mpsc para el paso seguro de mensajes.
  3. Mutabilidad Compartida con Arc y Mutex: Acceso seguro a datos compartidos.
  4. Programación Asíncrona con async y await: Ejecutando múltiples tareas concurrentemente sin bloqueo.
  5. Construcción de un Servidor de Chat Asíncrono: Integrando Tokio para manejar múltiples conexiones simultáneamente.

🧵 Concurrencia con threads en Rust #

Rust nos permite crear hilos seguros usando std::thread::spawn, evitando errores comunes en programación multihilo.

📌 Ejemplo: creación de threads #

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();
}

thread::spawn: Lanza un nuevo hilo. ✔ join(): Asegura que el hilo secundario termine antes de continuar.


📬 Comunicación segura entre hilos con mpsc #

Para evitar el acceso directo a memoria compartida, usamos canales de mensajería (mpsc):

use std::sync::mpsc;
use std::thread;

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

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

Evita condiciones de carrera al evitar compartir datos directamente.


🔒 Arc y mutex: sincronización de datos compartidos #

Cuando los hilos deben acceder a una variable compartida, combinamos Arc con Mutex para garantizar 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: Permite múltiples referencias a Mutex. ✔ Mutex: Evita condiciones de carrera al acceder a la variable compartida.


⚡ Programación asíncrona con async y await #

Rust nos permite ejecutar tareas concurrentemente sin bloquear el hilo principal.

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();
}

Ejecuta múltiples tareas sin bloqueo. ✔ tokio::spawn permite lanzar tareas asíncronas de manera eficiente.


🏗 Construcción de un servidor de chat asíncrono #

Combinamos todo lo aprendido para construir un servidor de chat que maneja múltiples clientes concurrentemente.

use tokio::net::{TcpListener, TcpStream};
use tokio::sync::broadcast;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};

#[Tokio::main]
async fn main() {
    let listener = TcpListener::bind("127.0.0.1:8080").await.unwrap();
    let (tx, _rx) = broadcast::channel(10);
    println!("Servidor de chat en 127.0.0.1:8080");
    
    loop {
        let (socket, _) = listener.accept().await.unwrap();
        let tx = tx.clone();
        let rx = tx.subscribe();
        tokio::spawn(async move {
            manejar_cliente(socket, tx, rx).await;
        });
    }
}

Manejo de múltiples conexiones sin bloqueo. ✔ Mensajería en tiempo real con broadcast::channel.


🏆 Conclusión #

Esta semana exploramos cómo Rust maneja la concurrencia y programación asíncrona de manera segura y eficiente.

🎯 Resumen de lo aprendido:Hilos (std::thread) para concurrencia básica. ✔ Mensajería con mpsc para comunicación segura. ✔ Sincronización con Arc<Mutex<T>> para datos compartidos. ✔ Programación asíncrona con async/await. ✔ Construcción de un servidor de chat asíncrono.

🔮 Próximo paso: Experimentar con arquitecturas asincrónicas avanzadas y optimizar el rendimiento de servidores concurrentes.

¡Hasta la próxima, Rustaceos!