mpsc::channel în Rust

Creează un canal prin care firele își trimit mesaje: Sender trimite cu send(), Receiver primește.

Sintaxă
use std::sync::mpsc;

pub fn channel<T>() -> (Sender<T>, Receiver<T>)
fn send(&self, t: T) -> Result<(), SendError<T>>

Ce face mpsc::channel

mpsc::channel() returnează o pereche (Sender, Receiver). Ce trimiți cu tx.send(valoare) ajunge, în ordinea trimiterii, la capătul rx. Valoarea este mutată prin canal, deci firul care a trimis-o nu o mai poate folosi.

„mpsc” înseamnă multiple producer, single consumer: Sender-ul se poate clona (tx.clone()) pentru mai multe fire care trimit, dar există un singur Receiver. Canalul nu are limită de mesaje; varianta cu limită este mpsc::sync_channel(n).

send returnează Err doar dacă Receiver-ul a dispărut. Când toate Sender-ele dispar, receptorul află că nu mai vin mesaje (bucla for m in rx se termină).

Ce returnează

channel: perechea (Sender<T>, Receiver<T>). send: Ok(()) sau Err(SendError(valoare)).

Exemple

Exemplul 1: Un fir trimite, main primește

Mesajele de la un singur Sender ajung în ordinea în care au fost trimise.

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

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

    let producator = thread::spawn(move || {
        for comanda in ["pizza", "supă", "salată"] {
            tx.send(comanda.to_string()).unwrap();
        }
    });

    for mesaj in rx {
        println!("bucătăria a primit: {mesaj}");
    }
    producator.join().unwrap();
    println!("nu mai vin comenzi");
}

Rezultat

bucătăria a primit: pizza
bucătăria a primit: supă
bucătăria a primit: salată
nu mai vin comenzi

Exemplul 2: Mai mulți producători

Fiecare fir are propriul Sender clonat. Mesajele de la fire diferite se amestecă în ordine variabilă, așa că le sortăm la final. drop(tx) închide originalul, altfel bucla nu s-ar termina.

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

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

    for (id, oras) in ["Iași", "Cluj", "Brașov"].into_iter().enumerate() {
        let tx = tx.clone();
        thread::spawn(move || {
            let temperatura = 10 + id as i32 * 3;
            tx.send(format!("{oras}: {temperatura}°C")).unwrap();
        });
    }
    drop(tx);

    let mut rapoarte: Vec<String> = rx.iter().collect();
    rapoarte.sort();
    for r in rapoarte {
        println!("{r}");
    }
}

Rezultat

Brașov: 16°C
Cluj: 13°C
Iași: 10°C

Greșeli frecvente

  • Uiți drop(tx) pentru Sender-ul original după ce ai dat clone firelor: for m in rx așteaptă la nesfârșit, pentru că un Sender încă există.
  • Folosești valoarea după send: a fost mutată prin canal. Trimite o copie sau un clone() dacă ai nevoie de ea în continuare.

Comenzi înrudite

  • Receiver: Capătul de primire al unui canal mpsc: recv() așteaptă următorul mesaj, try_recv() verifică fără să aștepte.
  • thread::spawn: Pornește un fir de execuție nou care rulează funcția dată; join() așteaptă terminarea lui și îi dă rezultatul.
  • Mutex: Protejează o valoare folosită din mai multe fire: lock() așteaptă până când firul curent este singurul cu acces.