El passatge de missatges és una tècnica de concurrència que permet que els fils de Rust es comuniquin entre ells de manera segura i eficient. En aquest tema, aprendrem com utilitzar canals (channels) per enviar dades entre fils.

Conceptes Clau

  1. Canals (channels): Són mecanismes de comunicació que permeten enviar dades d'un fil a un altre.
  2. Transmissor (Sender): La part del canal que envia dades.
  3. Receptor (Receiver): La part del canal que rep dades.

Creació d'un Canal

Per crear un canal, utilitzem la funció mpsc::channel del mòdul std::sync::mpsc. Aquesta funció retorna una tupla amb un transmissor (Sender) i un receptor (Receiver).

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

fn main() {
    // Creem un canal
    let (tx, rx) = mpsc::channel();

    // Llancem un fil que enviarà un missatge
    thread::spawn(move || {
        let val = String::from("Hola, món!");
        tx.send(val).unwrap();
    });

    // Rebem el missatge en el fil principal
    let received = rx.recv().unwrap();
    println!("Hem rebut: {}", received);
}

Explicació del Codi

  1. Creació del Canal: let (tx, rx) = mpsc::channel(); crea un transmissor (tx) i un receptor (rx).
  2. Fil de Transmissió: thread::spawn(move || { ... }) crea un nou fil que envia un missatge a través del transmissor.
  3. Enviament del Missatge: tx.send(val).unwrap(); envia el missatge. unwrap() s'utilitza per gestionar possibles errors.
  4. Recepció del Missatge: let received = rx.recv().unwrap(); rep el missatge. recv() bloqueja el fil fins que es rep un missatge.

Enviament de Múltiples Missatges

Podem enviar múltiples missatges a través del mateix canal. El receptor pot rebre'ls un per un.

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

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

    thread::spawn(move || {
        let vals = vec![
            String::from("primer"),
            String::from("segon"),
            String::from("tercer"),
        ];

        for val in vals {
            tx.send(val).unwrap();
            thread::sleep(Duration::from_secs(1));
        }
    });

    for received in rx {
        println!("Hem rebut: {}", received);
    }
}

Explicació del Codi

  1. Vector de Missatges: let vals = vec![ ... ]; crea un vector de missatges.
  2. Enviament en Bucle: for val in vals { ... } envia cada missatge amb una pausa d'un segon entre enviaments.
  3. Recepció en Bucle: for received in rx { ... } rep cada missatge a mesura que arriba.

Clonació del Transmissor

Podem clonar el transmissor per permetre que múltiples fils enviïn missatges al mateix receptor.

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

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

    thread::spawn(move || {
        tx.send(String::from("des del primer transmissor")).unwrap();
    });

    thread::spawn(move || {
        tx1.send(String::from("des del segon transmissor")).unwrap();
    });

    for received in rx {
        println!("Hem rebut: {}", received);
    }
}

Explicació del Codi

  1. Clonació del Transmissor: let tx1 = tx.clone(); crea una còpia del transmissor.
  2. Enviament des de Fils Diferents: Cada fil utilitza un transmissor diferent per enviar missatges.

Exercicis Pràctics

Exercici 1: Enviament i Recepció de Missatges

Crea un programa que llanci tres fils. Cada fil ha d'enviar un missatge diferent a través del mateix canal. El fil principal ha de rebre i imprimir tots els missatges.

Solució

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

fn main() {
    let (tx, rx) = mpsc::channel();
    let tx1 = tx.clone();
    let tx2 = tx.clone();

    thread::spawn(move || {
        tx.send(String::from("missatge del primer fil")).unwrap();
    });

    thread::spawn(move || {
        tx1.send(String::from("missatge del segon fil")).unwrap();
    });

    thread::spawn(move || {
        tx2.send(String::from("missatge del tercer fil")).unwrap();
    });

    for received in rx {
        println!("Hem rebut: {}", received);
    }
}

Exercici 2: Enviament de Múltiples Missatges amb Pauses

Crea un programa que llanci un fil que enviï cinc missatges amb una pausa de mig segon entre cada enviament. El fil principal ha de rebre i imprimir cada missatge a mesura que arriba.

Solució

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

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

    thread::spawn(move || {
        for i in 1..=5 {
            tx.send(format!("missatge {}", i)).unwrap();
            thread::sleep(Duration::from_millis(500));
        }
    });

    for received in rx {
        println!("Hem rebut: {}", received);
    }
}

Errors Comuns i Consells

  1. Bloqueig del Receptor: recv() bloqueja el fil fins que es rep un missatge. Si no s'envia cap missatge, el fil es quedarà bloquejat indefinidament.
  2. Gestió d'Errors: Utilitza unwrap() o gestiona els errors adequadament per evitar que el programa es bloquegi en cas d'errors en l'enviament o recepció de missatges.
  3. Clonació del Transmissor: Recorda clonar el transmissor si necessites enviar missatges des de múltiples fils.

Conclusió

El passatge de missatges és una tècnica poderosa per gestionar la concurrència en Rust. Utilitzant canals, podem comunicar-nos de manera segura entre fils, evitant molts dels problemes associats amb la concurrència tradicional. Amb la pràctica, aquesta tècnica esdevindrà una eina essencial en el teu arsenal de programació en Rust.

© Copyright 2024. Tots els drets reservats