Historisch war die Arbeit mit Multithreading von Abstürzen, Race Conditions und Speicherlecks begleitet, insbesondere bei unkontrolliertem Speicherzugriff. Rust implementiert das Konzept der Threadsicherheit auf Typenebene – ein Objekt kann nur dann an einen Thread übergeben werden, wenn es die erforderlichen Traits (Send, Sync) implementiert. Die Threads selbst werden über std::thread::spawn erstellt, und die Kommunikation zwischen ihnen erfolgt über Kanäle oder gemeinsam genutzten Speicher mit kontrollierter Mutation (Mutex, Arc).
Problem: Die manuelle Verwaltung der Synchronisierung ist komplex und gefährlich. Die Übertragung beliebiger Objekte zwischen Threads ohne explizite Besitzübertragung führt zu Race Conditions und Abstürzen.
Lösung: Nur explizit bewegliche (move) Objekte oder solche, die über Arc, Mutex geteilt werden, sowie integrierte Nachrichtenkanäle (std::sync::mpsc, crossbeam). Dies minimiert Fehler, die mit synchronem und asynchronem Datenaustausch verbunden sind: Der Besitz ist immer eindeutig.
Beispielcode:
use std::thread; use std::sync::mpsc; fn main() { let (tx, rx) = mpsc::channel(); thread::spawn(move || { tx.send(String::from("Hallo vom Thread!")).unwrap(); }); let received = rx.recv().unwrap(); println!("Empfangen: {}", received); }
Wichtige Merkmale:
Kann man nach der Übertragung eines Objekts über move weiterhin im Haupt-Thread verwenden?
Nein, sobald ein Objekt verschoben wurde (z.B. in einer Closure in thread::spawn), kann es im übergeordneten Thread nicht mehr verwendet werden, der Compiler wird den Code nicht kompilieren.
Kann man veränderbare Referenzen (&mut T) zwischen Threads übergeben?
Nein, eine veränderbare Referenz &mut T kann nur in einer Instanz existieren, und das Trait Send ist standardmäßig nicht darauf implementiert. Für die Arbeit mit veränderlichen Daten wird eine Wrapper über Mutex/Arc verwendet.
Warum kann Rc<T> nicht zur gemeinsamen Besitzübertragung zwischen Threads verwendet werden?
Rc<T> implementiert weder Sync noch Send, da sein interner Zähler nicht thread-sicher ist. Für thread-sichere Anwendungen wird Arc<T> (atomic reference counter) verwendet.
// Vergleich von Rc und Arc use std::sync::Arc; let x = Arc::new(5); // kann geklont und zwischen Threads geteilt werden
Ein Entwickler wollte eine Zeichenkette zwischen Threads mit Rc<String> teilen und steckt Rc in thread::spawn. Der Code wird nur kompiliert, wenn er über unsafe erzwungen wird, danach kann die Anwendung abstürzen oder mit beschädigten Daten arbeiten.
Vorteile:
Nachteile:
Verwendung von Arc<String> + Mutex<String> für geschützten Zugriff, oder Übertragung einer Nachricht über einen Kanal, ohne gemeinsamen Besitz.
Vorteile:
Nachteile: