Die Frage der sicheren Arbeit in Multithread-Umgebungen beschäftigt Programmierer schon lange, da sie ständig mit Problemen wie Datenrennen, inkonsistenten Daten und Speicherlecks konfrontiert sind. Rust hat einen einzigartigen Ansatz mit den Marker-Traits Send und Sync implementiert, um diese Probleme bereits zur Compile-Zeit zu minimieren.
Das Problem ist das Fehlen einer Zugriffskontrolle auf gemeinsame Daten zwischen Threads, was zu schwer zu diagnostizierenden Fehlern führt. In vielen Programmiersprachen liegt die Verantwortung vollständig beim Programmierer, während der Rust-Compiler selbst überprüft, was sicher zwischen Threads übergeben/geteilt werden kann.
Die Lösung: Der Trait Send garantiert die sichere Übertragung eines Objekts von einem Thread zu einem anderen. Sync ermöglicht den gleichzeitigen Zugriff auf eine Referenz auf ein Objekt aus verschiedenen Threads. Nahezu alle Standardtypen in Rust implementieren diese Traits automatisch, während benutzerdefinierte Typen diese manuell implementieren oder durch impl !Send oder impl !Sync für spezifische Fälle verbieten können.
Beispielcode:
use std::sync::{Arc, Mutex}; use std::thread; let counter = Arc::new(Mutex::new(0)); let mut handles = vec![]; for _ in 0..10 { let counter = Arc::clone(&counter); let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } // counter wird immer 10 betragen, ohne Datenrennen!
Wichtige Merkmale:
Kann ein Typ mit unsicheren Zeigern Send oder Sync sein?
Nein, wenn der Typ einen rohen Zeiger oder Ressourcen ohne Garantien für Thread-Sicherheit enthält, implementiert er diese Traits nicht, oder der Entwickler muss sie manuell mit voller Verantwortung implementieren (normalerweise mit unsafe impl Send/Sync).
Sind Rc<T> und RefCell<T> Send oder Sync?
Nein, Rc<T> und RefCell<T> sind nicht sicher für die Nutzung in Threads (weder Send noch Sync). Für Multithreading-Szenarien werden Arc<T> und Mutex/RwLock verwendet.
Was passiert, wenn eine static Variable einen Typ ohne implementiertes Sync enthält?
Rust erlaubt es nicht, dass eine solche static Variable existiert: Sie muss Sync sein, andernfalls gibt der Compiler einen Fehler aus.
unsafe impl Send/Sync ohne strikte Kontrolle.Ein junger Entwickler gibt ein Rc-Objekt an thread::spawn — der Code kompiliert nur, wenn Rc nicht zwischen Threads übergeben wird. Beim Versuch, Rc aus thread::spawn zu extrahieren, gibt es einen Kompilierungsfehler, da Rc Send nicht implementiert und nicht vor Datenrennen geschützt ist.
Vorteile:
Nachteile:
Es wird Arc+Mutex für einen Multithread-Zähler verwendet, alle Threads arbeiten mit denselben Daten über eine thread-sichere Schnittstelle. Es gibt keine Datenrennen, der Code ist sicher und stabil.
Vorteile:
Nachteile: