Canali mpsc
Invio tra thread
Canali mpsc è una lezione Learn Rust Coding gratuita su CoddyKit. Questa è la lezione 1 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Learn Rust Coding, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Learn Rust Coding include 4 lezioni in totale.
Che cos'è un canale?
Un channel è un canale unidirezionale per inviare valori da un thread a un altro. La libreria standard di Rust fornisce std::sync::mpsc, dove mpsc significa multiple producer, single consumer (più produttori, un singolo consumatore).
- La parte Sender inserisce i valori.
- La parte Receiver estrae i valori.
I canali consentono ai thread di comunicare tramite il passaggio di messaggi invece di condividere direttamente la memoria, evitando così molti data race.
Creare un canale
Chiamate mpsc::channel() per ottenere una tupla di (Sender, Receiver). In questo esempio inviamo un valore da un thread creato appositamente al thread principale.
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
tx.send(42).unwrap();
});
let received = rx.recv().unwrap();
println!("Got: {}", received);
}send e recv
tx.send(value) restituisce un Result: fallisce solo se il receiver è stato eliminato. rx.recv() si blocca finché non arriva un valore e restituisce Err quando non rimane alcun sender.
sendtrasferisce la proprietà del valore nel canale.recvprende la proprietà dall'altra parte.
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let msg = String::from("hello from thread");
tx.send(msg).unwrap();
});
let text = rx.recv().unwrap();
println!("{}", text);
}La proprietà attraversa il canale
Poiché send prende il valore per valore, non potete utilizzarlo dopo l'invio. Questa regola verificata in fase di compilazione garantisce che nessun thread mantenga un riferimento obsoleto a dati che ora appartengono a un altro thread.
Di seguito, provare a stampare msg dopo send causerebbe un errore di compilazione, quindi lo utilizziamo una sola volta.
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let data = vec![1, 2, 3];
tx.send(data).unwrap();
// data is moved; cannot use it here
});
let v = rx.recv().unwrap();
println!("Sum: {}", v.iter().sum::<i32>());
}Iterare su un Receiver
Un Receiver implementa IntoIterator. Iterandovi sopra ottenete ogni valore finché il canale non viene chiuso (quando tutti i sender vengono eliminati). Questo è il modo idiomatico per consumare un flusso di messaggi.
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
for i in 1..=3 {
tx.send(i).unwrap();
}
});
for received in rx {
println!("Received: {}", received);
}
}Più produttori con clone
La sigla mp in mpsc indica che potete avere molti sender. Clonate il Sender e date una copia a ogni thread. Il receiver raccoglie tutto finché tutte le copie non vengono eliminate.
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
let tx2 = tx.clone();
thread::spawn(move || { tx.send("from A").unwrap(); });
thread::spawn(move || { tx2.send("from B").unwrap(); });
for msg in rx {
println!("{}", msg);
}
}Semantica della chiusura del canale
Il ciclo del receiver termina automaticamente quando vengono eliminati tutti i sender. Se anche un solo Sender rimane attivo, for msg in rx si blocca per sempre in attesa di altri messaggi. Eliminate sempre i sender o limitatene correttamente l'ambito per consentire la conclusione del ciclo.
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
let handle = thread::spawn(move || {
for i in 0..3 {
tx.send(i * 10).unwrap();
}
// tx dropped here, closing the channel
});
handle.join().unwrap();
let total: i32 = rx.iter().sum();
println!("Total: {}", total);
}try_recv per letture non bloccanti
recv si blocca, mentre try_recv restituisce immediatamente un Result. Restituisce Ok(value) se un messaggio è pronto oppure Err se il canale è vuoto o disconnesso. È utile nei cicli di eventi che devono continuare a svolgere altre attività.
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
thread::sleep(Duration::from_millis(50));
tx.send("ready").unwrap();
});
loop {
match rx.try_recv() {
Ok(msg) => { println!("{}", msg); break; }
Err(_) => println!("waiting..."),
}
thread::sleep(Duration::from_millis(20));
}
}Inviare tipi personalizzati
Qualsiasi tipo che implementa Send può viaggiare attraverso un canale, incluse le vostre struct e i vostri enum. Gli enum sono ottimi per modellare diversi tipi di messaggio in un protocollo per worker.
use std::sync::mpsc;
use std::thread;
enum Job {
Print(String),
Add(i32, i32),
}
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
tx.send(Job::Print(String::from("hi"))).unwrap();
tx.send(Job::Add(2, 3)).unwrap();
});
for job in rx {
match job {
Job::Print(s) => println!("print: {}", s),
Job::Add(a, b) => println!("add: {}", a + b),
}
}
}sync_channel e backpressure
mpsc::sync_channel(n) crea un canale limitato con una dimensione del buffer pari a n. Quando il buffer è pieno, send si blocca finché non si libera spazio. In questo modo ottenete il backpressure, impedendo a un produttore veloce di sovraccaricare un consumatore lento.
sync_channel(0)è un canale rendezvous: send e recv devono incontrarsi.
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::sync_channel(2);
thread::spawn(move || {
for i in 1..=4 {
tx.send(i).unwrap();
println!("sent {}", i);
}
});
for v in rx {
println!("got {}", v);
}
}Un semplice modello worker
I canali sono ideali per il modello produttore/consumatore: un thread produce elementi di lavoro, mentre un altro li consuma e li elabora. Qui il thread principale produce numeri e un worker eleva al quadrato ciascuno di essi, comunicando poi il risultato tramite un secondo canale.
use std::sync::mpsc;
use std::thread;
fn main() {
let (job_tx, job_rx) = mpsc::channel();
let (res_tx, res_rx) = mpsc::channel();
thread::spawn(move || {
for n in job_rx {
res_tx.send(n * n).unwrap();
}
});
for n in 1..=4 {
job_tx.send(n).unwrap();
}
drop(job_tx);
for r in res_rx {
println!("square: {}", r);
}
}Verifica rapida
Verificate la vostra comprensione dei canali mpsc.
Riepilogo
Avete imparato i fondamenti dei canali mpsc:
mpsc::channel()restituisce una coppia(Sender, Receiver).sendtrasferisce un valore nel canale;recvsi blocca per estrarlo.- Iterare su un receiver consuma i messaggi finché tutti i sender non vengono eliminati.
- Clonate il
Senderper avere più produttori. try_recvnon è bloccante;sync_channel(n)aggiunge un backpressure limitato.
I canali consentono ai thread di condividere i dati in sicurezza trasferendo la proprietà invece di condividere la memoria.
Domande Frequenti
La lezione «Canali mpsc» è gratuita?
Sì — il testo completo di «Canali mpsc» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Learn Rust Coding, passa a CoddyKit PRO. Il corso Learn Rust Coding include 4 lezioni in totale.
Cosa imparerò in «Canali mpsc»?
Invio tra thread Eserciti Learn Rust Coding con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare Learn Rust Coding?
Non è richiesta alcuna esperienza precedente. Learn Rust Coding su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 1 di 4.
Quanto tempo richiede la lezione «Canali mpsc»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione Learn Rust Coding?
Sì. Ogni lezione Learn Rust Coding include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.