Skip to content

Modul 12 — Concurrency & Threads

Threads, Channels, Mutex: Rusts Ownership-Regeln schützen dich auch parallel — Datenrennen werden zu Compile-Fehlern.

Fortschritt0/4 Lektionen
45

Threads: spawn, move, join

Lektion 45 von 4

Ein Programm kann Arbeit auf mehrere Kerne verteilen: Threads. Der Start ist ein Funktionsaufruf mit einer Closure:

use std::thread;

fn main() {
    let daten = vec![1, 2, 3, 4];

    let handle = thread::spawn(move || {
        let summe: i32 = daten.iter().sum();
        summe * 10
    });

    println!("Ergebnis: {}", handle.join().unwrap());
}

Drei Bausteine: spawn startet die Closure auf einem neuen Thread, move übergibt die gefangenen Daten als Besitz (nicht als Leihe — der Thread könnte den erzeugenden Scope überleben, also würde jede Referenz baumeln), und join() wartet auf das Ende und liefert das Ergebnis. Vergisst du join, endet main möglicherweise zuerst — und der Prozess nimmt den Thread mit ins Grab, bevor er fertig war.

Warum move bei Threads Pflicht ist

  • Der Thread kann länger leben als die erzeugende Funktion
  • Geliehene Daten könnten dangling werden — der Compiler verbietet das
  • move überträgt Besitz: die Daten gehören ab jetzt dem Thread
  • Das ist keine Einschränkung, sondern die Datenrennen-Prävention
Thread-WerkstattBereit
Editor wird geladen…

Übung: spawn + join

🧩 Übung m12-l1-e1

🔮 Vorhersage: Was passiert beim Ausführen?

Editor wird geladen…
KI-Tutor (sokratisch — keine Komplettlösungen)

✅ Wichtige Erkenntnisse

0/3

Haken setzen, um deinen Lernfortschritt zu markieren:

46

Channels: Kommunikation statt Teilung

Lektion 46 von 4

Der sauberste Weg, Threads Daten austauschen zu lassen: Channels — eine Einbahnstraße für Nachrichten. Go hat den Spruch geprägt: "Do not communicate by sharing memory; share memory by communicating."

use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        for n in 1..=3 {
            tx.send(n * n).unwrap();
        }
        drop(tx);
    });

    while let Ok(wert) = rx.recv() {
        println!("{wert}");
    }
}

mpsc heißt: multiple producers, single consumer — viele Sender (Sender clonen), ein Empfänger. send überträgt Besitz der Nachricht, recv wartet blockierend. Und das Ende ist im Typ verankert: gehen alle Sender verloren, liefert recv ein Err — der while-let-Loop endet von selbst. Kein Polling, keine Magic-Values: die Kanal-Schließung IST ein Wert.

Übung: der Quadrat-Kanal

🧩 Übung m12-l2-e1

🔮 Vorhersage: Was passiert beim Ausführen?

Editor wird geladen…
KI-Tutor (sokratisch — keine Komplettlösungen)

✅ Wichtige Erkenntnisse

0/3

Haken setzen, um deinen Lernfortschritt zu markieren:

47

Geteilter Zustand: Mutex + Arc

Lektion 47 von 4

Manchmal willst du Daten nicht kopieren oder schicken, sondern wirklich teilen. Dann braucht es zwei Teile: einen Schloss-Mechanismus und einen thread-sicheren Besitz.

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let zaehler = Arc::new(Mutex::new(0u32));

    let mut handles = Vec::new();
    for _ in 0..4 {
        let z = Arc::clone(&zaehler);
        handles.push(thread::spawn(move || {
            *z.lock().unwrap() += 10;
        }));
    }
    for h in handles {
        h.join().unwrap();
    }
    println!("Zähler: {}", *zaehler.lock().unwrap());
}

Mutex ist das Schloss: lock() gibt dir einen Guard — nur über ihn erreichst du die Daten, und beim Drop des Guards gibt er das Schloss frei. Das Schloss-Umgehen ist unmöglich, die Daten liegen ja EINGESPERRT im Mutex. Arc ist Rc mit atomarem Zähler — der einzige Grund, warum dieselbe Struktur über Threads geteilt werden darf.

Beides zusammen ist die Standard-Antwort auf "vier Threads, ein Zähler" — und der Compiler erzwingt die Kombination: Rc-Container verweigert er mit E0277, ein Mutex ohne Teilung wäre sinnlos.

Fearless Concurrency konkret

  • Datenrennen (data races) sind in safe Rust Compile-Fehler, keine crash bugs
  • Send/Sync-Traits beschreiben, welche Typen thread-überqueren dürfen
  • MutexGuard = scoped locking: keine vergessenen unlocks
  • Deadlock-Risiko bleibt — aber Rennen verhindert der Compiler

Übung: der geteilte Zähler

🧩 Übung m12-l3-e1

🔮 Vorhersage: Was passiert beim Ausführen?

Editor wird geladen…
KI-Tutor (sokratisch — keine Komplettlösungen)

✅ Wichtige Erkenntnisse

0/3

Haken setzen, um deinen Lernfortschritt zu markieren:

48

Mini-Projekt: die parallele Summe

Lektion 48 von 4

Der Abschluss: eine Million Zahlen, zwei Kerne, eine Antwort. Das Szenario:

Praxis-Szenario: Die Auswertung, die ewig dauert

Dein Team wertet riesige Messreihen aus — seriell, in einer Schleife, sekundenlang. Auf einem Mehrkern-Rechner arbeitet dabei 1 von 8 Kernen. Der Vorschlag: die Datenreihe halbieren und beide Hälften parallel summieren. Aber Vorsicht — naive Ansätze mit geteilten Variablen enden in Datenrennen oder Compile-Fehlern. Genau das soll die Lösung vermeiden, nicht nur vermeiden lassen.

🧩 Übung m12-l4-e1

🔮 Vorhersage: Was passiert beim Ausführen?

Editor wird geladen…
KI-Tutor (sokratisch — keine Komplettlösungen)

Was du in Modul 12 geschafft hast

  • Threads mit spawn/move/join — Besitz als Ticket in den Nebenlauf
  • Channels: Nachrichten mit Besitz-Übergabe, Ende im Typ verankert
  • Mutex + Arc für den unvermeidlichen geteilten Zustand
  • Parallelisierung durch Aufteilen statt Synchronisieren

✅ Wichtige Erkenntnisse

0/3

Haken setzen, um deinen Lernfortschritt zu markieren:

Modul-Checkpoint

Checkpoint: Modul 12

Bestehensgrenze: 80 %. Der Versuch wird bewertet und zählt für das Modul-Gate.

This site uses essential cookies for Stripe payments. No tracking cookies.