Kanały Crossbeam
Zaawansowane kanały
Kanały Crossbeam to bezpłatna lekcja Learn Rust Coding na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Learn Rust Coding, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Learn Rust Coding zawiera 4 lekcji w sumie.
Poza standardowym mpsc
Standardowy kanał mpsc ma jednego odbiorcę: tylko jeden receiver. Crate crossbeam-channel oferuje kanały z wieloma producentami i wieloma odbiorcami (mpmc), zapewniające więcej funkcji i często lepszą wydajność.
Najważniejsze cechy:
- Możliwość klonowania obiektów
Receiver, a nie tylko nadajników. - Potężne makro
select!do oczekiwania na wiele kanałów. - Kanały ograniczone, nieograniczone oraz specjalne kanały, takie jak ticki.
Dodawanie zależności
Crossbeam jest zewnętrznym crate'em, dlatego należy dodać go do pliku Cargo.toml:
[dependencies]crossbeam-channel = "0.5"
Następnie należy zaimportować jego funkcje. Ponieważ wymaga to Cargo i zewnętrznego crate'a, przedstawione tutaj fragmenty pokazują API, ale nie działają samodzielnie.
// 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());
}Kanały unbounded i bounded
Crossbeam oferuje dwa główne konstruktory:
unbounded()zwiększa rozmiar w miarę potrzeb;sendnigdy nie blokuje działania.bounded(cap)ma stały bufor;sendblokuje działanie, gdy bufor jest pełny, zapewniając mechanizm backpressure.
Kanał bounded(0) jest kanałem rendezvous, w którym send i recv przekazują dane bezpośrednio.
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);
}
}Wielu odbiorców
Najważniejsza zaleta: można klonować obiekt Receiver. Kilka wątków roboczych może pobierać dane z tego samego kanału, a każdy komunikat trafia dokładnie do jednego z nich. Jest to podstawa puli wątków ze współdzieleniem pracy.
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(); }
}Makro select!
select! pozwala jednemu wątkowi oczekiwać na operacje na wielu kanałach i zareagować na tę, która jako pierwsza będzie gotowa. Działa podobnie jak match dla zdarzeń kanałów i idealnie nadaje się do łączenia danych wejściowych z wielu źródeł.
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()),
}
}
}Limity czasu za pomocą select!
select! obsługuje gałąź default oraz gałąź czasomierza recv(after(duration)), dzięki czemu można przerwać oczekiwanie po upływie limitu czasu zamiast blokować działanie w nieskończoność. Jest to niezbędne w systemach wymagających szybkiej reakcji.
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 i try_recv
Podobnie jak biblioteka standardowa, crossbeam udostępnia warianty nieblokujące. try_send natychmiast kończy się błędem, jeśli kanał ograniczony jest pełny, a try_recv natychmiast kończy się błędem, jeśli żaden komunikat nie jest gotowy. Obie funkcje zwracają opisowy błąd, który można obsłużyć za pomocą 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 i praca okresowa
Funkcja tick(duration) zwraca receiver, który dostarcza komunikat w stałych odstępach czasu. Można połączyć ją z select!, aby wykonywać zadania okresowe równolegle z obsługą innych kanałów, na przykład sygnał heartbeat lub pętlę odpytywania.
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);
}
}
}
}Potok etapów
Crossbeam doskonale sprawdza się przy budowaniu potoków: pierwszy etap produkuje dane, drugi je przekształca, a trzeci odbiera. Każdy etap działa we własnym wątku połączonym kanałami, a klonowanie odbiorników pozwala skalować dowolny etap do wielu pracowników.
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); }
}Kiedy Crossbeam jest lepszy od std
Wybierz crossbeam-channel, gdy potrzebujesz:
- Wielu odbiorników współdzielących jedną kolejkę (pule pracowników).
select!dla wielu kanałów z limitami czasu.- Timerów okresowych za pomocą
tick, zintegrowanych z wybieraniem.
W przypadku prostego potoku jeden producent–jeden konsument standardowy mpsc w zupełności wystarczy i nie wymaga dodatkowej zależności.
Kanały są zamykane przez usuwanie
Podobnie jak w std, kanały crossbeam zamykają się, gdy usunięci zostaną wszyscy nadawcy (po stronie odbiornika) lub wszyscy odbiorcy (po stronie nadawcy). Iterowanie po odbiorniku kończy się po usunięciu ostatniego nadawcy. Zawsze używaj drop dla nadawców albo ograniczaj ich zakres, aby pętle pracowników mogły poprawnie się zakończyć.
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();
}Szybkie sprawdzenie
Sprawdź swoją wiedzę o kanałach crossbeam.
Podsumowanie
Omówiono kanały crossbeam:
- Są kanałami mpmc: można klonować zarówno nadawców, jak i odbiorców.
unbounded()ibounded(n)sterują buforowaniem oraz mechanizmem backpressure.select!oczekuje na wiele kanałów, a limity czasu zapewniająafteri okresowytick.try_send/try_recvnie blokują wykonania.- Używaj crossbeam do pul pracowników i złożonych potoków, a std mpsc do prostych potoków.
Często zadawane pytania
Czy lekcja „Kanały Crossbeam” jest bezpłatna?
Tak — pełny tekst „Kanały Crossbeam” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Learn Rust Coding, przejdź na CoddyKit PRO. Kurs Learn Rust Coding zawiera 4 lekcji w sumie.
Co nauczysz się w „Kanały Crossbeam”?
Zaawansowane kanały Ćwiczysz Learn Rust Coding z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Learn Rust Coding?
Nie wymagamy żadnego doświadczenia. Learn Rust Coding w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.
Ile czasu zajmuje lekcja „Kanały Crossbeam”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Learn Rust Coding?
Tak. Każda lekcja Learn Rust Coding zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.