0Pricing
Learn Rust Coding · Lekcja

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; send nigdy nie blokuje działania.
  • bounded(cap) ma stały bufor; send blokuje 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() i bounded(n) sterują buforowaniem oraz mechanizmem backpressure.
  • select! oczekuje na wiele kanałów, a limity czasu zapewniają after i okresowy tick.
  • try_send/try_recv nie 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.

Wszystkie lekcje w tym kursie

  1. Kanały mpsc
  2. Współdzielenie stanu za pomocą Arc/Mutex
  3. Wątki zakresowe
  4. Kanały Crossbeam
← Powrót do Learn Rust Coding