Canali Crossbeam
Canali avanzati
Canali Crossbeam è una lezione Learn Rust Coding gratuita su CoddyKit. Questa è la lezione 4 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.
Oltre std mpsc
Il channel standard mpsc ha un singolo consumatore: esiste un solo ricevitore. Il crate crossbeam-channel offre channel multi-producer, multi-consumer (mpmc), con più funzionalità e prestazioni spesso migliori.
Punti salienti:
Receiverclonabili, non solo i sender.- Una potente macro
select!per attendere su più channel. - Channel con capacità limitata, illimitata e speciali, come quelli a tick.
Aggiungere la dipendenza
Crossbeam è un crate esterno, quindi aggiungetelo a Cargo.toml:
[dependencies]crossbeam-channel = "0.5"
Importate quindi le relative funzioni. Poiché è necessario Cargo e un crate esterno, gli snippet qui illustrano l'API invece di essere eseguibili autonomamente.
// Cargo.toml
// [dependencies]
// crossbeam-channel = "0.5"
use crossbeam_channel::unbounded;
fn main() {
let (s, r) = unbounded();
s.send("hello").unwrap();
println!("{}", r.recv().unwrap());
}unbounded e bounded
Crossbeam offre due costruttori principali:
unbounded()cresce secondo necessità;sendnon si blocca mai.bounded(cap)ha un buffer di dimensione fissa;sendsi blocca quando è pieno, fornendo backpressure.
Un channel bounded(0) è un channel rendezvous, in cui send e recv trasferiscono direttamente il messaggio.
use crossbeam_channel::bounded;
use std::thread;
fn main() {
let (s, r) = bounded(2);
thread::spawn(move || {
for i in 1..=3 {
s.send(i).unwrap();
}
});
while let Ok(v) = r.recv() {
println!("got {}", v);
}
}Più consumatori
Il vantaggio principale è la possibilità di clonare il Receiver. Diversi thread worker possono prelevare dallo stesso channel e ogni messaggio viene consegnato esattamente a uno di essi. Questa è la base di un thread pool con work stealing.
use crossbeam_channel::unbounded;
use std::thread;
fn main() {
let (s, r) = unbounded();
let mut workers = vec![];
for id in 0..3 {
let rx = r.clone();
workers.push(thread::spawn(move || {
while let Ok(job) = rx.recv() {
println!("worker {} got job {}", id, job);
}
}));
}
for job in 0..6 { s.send(job).unwrap(); }
drop(s);
for w in workers { w.join().unwrap(); }
}La macro select!
select! consente a un thread di attendere più operazioni sui channel e di agire su quella che diventa disponibile per prima. È simile a un match sugli eventi dei channel, ideale per combinare input provenienti da più sorgenti.
use crossbeam_channel::{unbounded, select};
use std::thread;
fn main() {
let (s1, r1) = unbounded();
let (s2, r2) = unbounded();
thread::spawn(move || { s1.send("from one").unwrap(); });
thread::spawn(move || { s2.send("from two").unwrap(); });
for _ in 0..2 {
select! {
recv(r1) -> msg => println!("r1: {}", msg.unwrap()),
recv(r2) -> msg => println!("r2: {}", msg.unwrap()),
}
}
}Timeout con select!
select! supporta un ramo default e un ramo timer recv(after(duration)), così potete smettere di attendere dopo un timeout invece di bloccarvi per sempre. È essenziale per i sistemi reattivi.
use crossbeam_channel::{unbounded, select, after};
use std::time::Duration;
fn main() {
let (_s, r) = unbounded::<i32>();
select! {
recv(r) -> msg => println!("received {:?}", msg),
recv(after(Duration::from_millis(100))) -> _ => {
println!("timed out waiting for a message");
}
}
}try_send e try_recv
Come la libreria standard, crossbeam fornisce varianti non bloccanti. try_send fallisce immediatamente se un channel con capacità limitata è pieno; try_recv fallisce immediatamente se non è pronto alcun messaggio. Entrambi restituiscono un errore descrittivo su cui potete usare un match.
use crossbeam_channel::{bounded, TrySendError};
fn main() {
let (s, _r) = bounded(1);
s.send(1).unwrap();
match s.try_send(2) {
Ok(()) => println!("sent"),
Err(TrySendError::Full(v)) => println!("full, kept {}", v),
Err(TrySendError::Disconnected(v)) => println!("closed, kept {}", v),
}
}tick e lavoro periodico
La funzione tick(duration) restituisce un ricevitore che consegna un messaggio a intervalli fissi. Combinate la funzione con select! per eseguire attività periodiche insieme ad altri channel, ad esempio un heartbeat o un ciclo di polling.
use crossbeam_channel::{tick, select};
use std::time::Duration;
fn main() {
let ticker = tick(Duration::from_millis(50));
let mut count = 0;
while count < 3 {
select! {
recv(ticker) -> _ => {
count += 1;
println!("tick {}", count);
}
}
}
}Una pipeline di fasi
Crossbeam eccelle nella costruzione di pipeline: la prima fase produce, la seconda trasforma e la terza consuma. Ogni fase viene eseguita sul proprio thread, collegato agli altri tramite channel; i receiver clonati consentono di scalare qualsiasi fase su più worker.
use crossbeam_channel::unbounded;
use std::thread;
fn main() {
let (in_s, in_r) = unbounded();
let (out_s, out_r) = unbounded();
thread::spawn(move || {
for n in in_r { out_s.send(n * n).unwrap(); }
});
for n in 1..=4 { in_s.send(n).unwrap(); }
drop(in_s);
for sq in out_r { println!("square: {}", sq); }
}Quando Crossbeam è meglio di std
Scegliete crossbeam-channel quando avete bisogno di:
- Più consumatori che condividono una coda (pool di worker).
select!su molti channel con timeout.- Timer periodici tramite
tickintegrati nella selezione.
Per una semplice pipe con un produttore e un consumatore, mpsc standard è sufficiente e non richiede dipendenze.
I channel si chiudono eliminandoli
Come quelli di std, i channel di crossbeam si chiudono quando vengono eliminati tutti i sender (dal lato del ricevitore) o tutti i receiver (dal lato del sender). L'iterazione su un receiver termina quando viene eliminato l'ultimo sender. Usate sempre drop oppure limitate l'ambito dei sender, così i cicli dei worker possono terminare correttamente.
use crossbeam_channel::unbounded;
use std::thread;
fn main() {
let (s, r) = unbounded();
let h = thread::spawn(move || {
let total: i32 = r.iter().sum();
println!("total: {}", total);
});
for i in 1..=5 { s.send(i).unwrap(); }
drop(s); // closes channel so the loop ends
h.join().unwrap();
}Verifica rapida
Verificate la vostra comprensione dei channel di crossbeam.
Riepilogo
Avete esplorato i channel di crossbeam:
- Sono mpmc: sia i sender sia i receiver possono essere clonati.
unbounded()ebounded(n)controllano il buffering e la backpressure.select!attende su più channel, con timeout tramiteaftere tick periodici tramitetick.try_send/try_recvsono non bloccanti.- Usate crossbeam per pool di worker e pipeline complesse; std mpsc per pipe semplici.
Domande Frequenti
La lezione «Canali Crossbeam» è gratuita?
Sì — il testo completo di «Canali Crossbeam» è 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 Crossbeam»?
Canali avanzati 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 4 di 4.
Quanto tempo richiede la lezione «Canali Crossbeam»?
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.