0Pricing
Learn Rust Coding · Lektion

mpsc-Kanäle

Zwischen Threads senden

mpsc-Kanäle ist eine kostenlose Learn Rust Coding-Lektion auf CoddyKit. Dies ist Lektion 1 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Learn Rust Coding-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Learn Rust Coding-Kurs umfasst insgesamt 4 Lektionen.

Was ist ein Channel?

Ein Channel ist eine Einweg-Pipe, über die Werte von einem Thread zu einem anderen gesendet werden. Rusts Standardbibliothek stellt std::sync::mpsc bereit, wobei mpsc für multiple producer, single consumer steht.

  • Die Sender-Hälfte schiebt Werte hinein.
  • Die Receiver-Hälfte holt Werte heraus.

Channels ermöglichen die Kommunikation zwischen Threads durch Nachrichtenübermittlung, statt Speicher direkt gemeinsam zu nutzen. Dadurch werden viele Data Races vermieden.

Einen Channel erstellen

Rufen Sie mpsc::channel() auf, um ein Tupel aus (Sender, Receiver) zu erhalten. Hier senden wir einen Wert von einem gestarteten Thread zurück an den Hauptthread.

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 und recv

tx.send(value) liefert ein Result: Es schlägt nur fehl, wenn der Receiver verworfen wurde. rx.recv() blockiert, bis ein Wert eintrifft, und liefert Err, wenn alle Sender verschwunden sind.

  • send überträgt den Besitz des Werts in den Channel.
  • recv übernimmt den Besitz auf der anderen Seite.
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);
}

Besitz wird durch den Channel übertragen

Da send den Wert per Wert übernimmt, können Sie ihn nach dem Senden nicht mehr verwenden. Diese Regel zur Compile-Zeit garantiert, dass kein Thread eine veraltete Referenz auf Daten behält, die nun einem anderen Thread gehören.

Der Versuch, msg nach send auszugeben, wäre unten ein Compile-Fehler. Daher verwenden wir die Nachricht nur einmal.

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>());
}

Über einen Receiver iterieren

Ein Receiver implementiert IntoIterator. Eine Schleife darüber liefert jeden Wert, bis der Channel geschlossen wird (alle Sender verworfen wurden). Dies ist die idiomatische Art, einen Nachrichtenstrom zu verarbeiten.

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);
    }
}

Mehrere Producer mit clone

Das mp in mpsc bedeutet, dass Sie viele Sender haben können. Klonen Sie den Sender und geben Sie jedem Thread eine Kopie. Der Receiver sammelt alle Werte, bis jeder Klon verworfen wurde.

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);
    }
}

Semantik beim Schließen eines Channels

Die Receiver-Schleife endet automatisch, wenn alle Sender verworfen wurden. Solange auch nur ein Sender aktiv bleibt, blockiert for msg in rx für immer und wartet auf weitere Werte. Verwerfen oder begrenzen Sie Sender immer korrekt, damit die Schleife beendet werden kann.

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 für nicht blockierendes Lesen

recv blockiert, aber try_recv liefert sofort ein Result. Bei einer bereiten Nachricht erhalten Sie Ok(value), bei einem leeren oder getrennten Channel Err. Das ist nützlich in Eventschleifen, die weiterhin andere Aufgaben erledigen müssen.

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));
    }
}

Benutzerdefinierte Typen senden

Jeder Typ, der Send implementiert, kann durch einen Channel übertragen werden, auch Ihre eigenen Strukturen und Enums. Enums eignen sich hervorragend, um unterschiedliche Nachrichtenarten in einem Worker-Protokoll zu modellieren.

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 und Backpressure

mpsc::sync_channel(n) erstellt einen begrenzten Channel mit der Puffergröße n. Wenn der Puffer voll ist, blockiert send, bis wieder Platz frei wird. Dadurch entsteht Backpressure, die verhindert, dass ein schneller Producer einen langsamen Consumer überlastet.

  • sync_channel(0) ist ein Rendezvous-Channel: send und recv müssen zusammentreffen.
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);
    }
}

Ein einfaches Worker-Muster

Channels eignen sich hervorragend für das Producer/Consumer-Muster: Ein Thread erzeugt Arbeitselemente, ein anderer konsumiert und verarbeitet sie. Hier erzeugt der Haupt-Thread Zahlen, und ein Worker quadriert jede Zahl und meldet das Ergebnis über einen zweiten Channel zurück.

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);
    }
}

Kurzer Test

Testen Sie Ihr Verständnis von mpsc-Channels.

Zusammenfassung

Sie haben die Grundlagen der mpsc-Channels gelernt:

  • mpsc::channel() gibt ein (Sender, Receiver)-Paar zurück.
  • send verschiebt einen Wert in den Channel; recv blockiert, um ihn herauszunehmen.
  • Das Iterieren über einen Receiver konsumiert Nachrichten, bis alle Sender verworfen wurden.
  • Duplizieren Sie den Sender für mehrere Producer.
  • try_recv blockiert nicht; sync_channel(n) fügt begrenzten Rückstau hinzu.

Channels ermöglichen Threads, Daten sicher gemeinsam zu nutzen, indem sie Besitz übertragen, statt Speicher gemeinsam zu verwenden.

Häufig gestellte Fragen

Ist die Lektion „mpsc-Kanäle“ kostenlos?

Ja — der vollständige Text von „mpsc-Kanäle“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Learn Rust Coding-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Learn Rust Coding-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „mpsc-Kanäle“?

Zwischen Threads senden Du übst Learn Rust Coding mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um Learn Rust Coding zu starten?

Keine Vorkenntnisse erforderlich. Learn Rust Coding auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 1 von 4.

Wie lange dauert die Lektion „mpsc-Kanäle“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser Learn Rust Coding-Lektion Code schreiben und ausführen?

Ja. Jede Learn Rust Coding-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. mpsc-Kanäle
  2. State mit Arc/Mutex teilen
  3. Bereichsbegrenzte Threads
  4. Crossbeam-Kanäle
← Zurück zu Learn Rust Coding