ProgrammierungSystembibliotheksentwickler / Backend-Entwickler

Wie funktioniert die "Thread-Sicherheit" (Send und Sync) in Rust und wie können diese für benutzerdefinierte Typen implementiert/beschränkt werden?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort.

In Rust wird die Sicherheit bei der Arbeit mit Threads durch zwei automatische Traits gewährleistet: Send und Sync.

  • Send ermöglicht die Übertragung eines Typs zwischen Threads (durch den Eigentumsübergang).
  • Sync garantiert, dass ein Typ sicher aus mehreren Threads gleichzeitig verwendet werden kann (durch Referenzen).

Die meisten Standardtypen in Rust implementieren standardmäßig diese Traits. Zum Beispiel Arc<T>, Mutex<T>Send und Sync (wenn T diese Traits ebenfalls erfüllt).

Sie können diese Traits explizit für Ihre Typen verbieten oder implementieren. Zum Beispiel, wenn Sie ein internes unsicheres Feld haben (wie einen rohen Zeiger oder externe Ressourcen), sollten Sie die Typen !Send oder !Sync machen:

use std::marker::PhantomData; use std::rc::Rc; struct MyType { not_thread_safe: Rc<u32>, _marker: PhantomData<*const ()>, } // Rc<u32> implementiert nicht Send/Sync, daher wird MyType auch diese Traits nicht implementieren.

Wenn Sie eine Low-Level-Hülle implementieren und die Thread-Sicherheit manuell steuern, können Sie Send/Sync manuell (unsafe) implementieren:

unsafe impl Send for MyType {} unsafe impl Sync for MyType {}

Es liegt in der Verantwortung des Programmierers, Garantien bezüglich der Thread-Sicherheit zu geben.

Fangfrage.

Was passiert, wenn Sie Rc<T> in mehrere Threads über Arc<Rc<T>> herausgeben?

Es wird oft gedacht, dass Arc alles schützt. Aber Rc<T> implementiert nicht Send/Sync, selbst wenn es in Arc eingewickelt ist! So:

use std::rc::Rc; use std::sync::Arc; fn main() { let data = Arc::new(Rc::new(5)); // std::thread::spawn(move || { // println!("{:?}", data); // }); // Der Compiler lässt das nicht zu! }

Arc kompensiert nicht das Fehlen von Send/Sync bei den enthaltenen Mitgliedern.

Beispiele für reale Fehler aufgrund mangelnden Wissens über die Feinheiten des Themas.


Geschichte

In einem Projekt gab es den Versuch, Arc<Rc<T>> zu verwenden, um Daten zwischen Threads zu teilen und das Eigentum nicht blockierend zu teilen. Das Programm stürzte während der Ausführung mit unvorhersehbarem Verhalten ab; es stellte sich heraus, dass Rc nicht threadsicher ist und zunächst das Wissen über die Traits Send/Sync fehlte.


Geschichte

Im von uns erstellten Event-Loop hielt der Typ State einen rohen Zeiger auf Daten. Der Typ State wurde mit unsafe impl Send markiert, aber es wurde vergessen, die Synchronisierung einzufügen. Schließlich trat ein klassisches Datenrennen auf, das bereits nach der Veröffentlichung festgestellt wurde.


Geschichte

Der Entwickler implementierte eine Newtype-Hülle um Mutex, machte sie aber fälschlicherweise !Sync und implementierte Sync nicht manuell. Dies hinderte die Verwendung des Typs im Multithread-Kontext (z.B. innerhalb von Arc<Mutex<T>>), da der Compiler Sync verlangte. Behebt durch Implementierung von unsafe impl Sync und Sicherheitsanalyse.