W Rust bezpieczeństwo w pracy z wątkami zapewniają dwa automatyczne trait: Send i Sync.
Send pozwala na przekazywanie typu między wątkami (poprzez przekazanie własności).Sync gwarantuje, że typ można bezpiecznie używać z wielu wątków jednocześnie (poprzez odniesienia).Większość standardowych typów w Rust domyślnie wdraża te traity. Na przykład, Arc<T>, Mutex<T> — Send i Sync (jeśli T również odpowiada tym traitom).
Możesz jawnie zabronić lub wdrożyć te traity dla swoich typów. Na przykład, jeśli masz wewnętrzne pole niebezpieczne (takie jak surowy wskaźnik lub zewnętrzne zasoby), powinieneś uczynić typy !Send lub !Sync:
use std::marker::PhantomData; use std::rc::Rc; struct MyType { not_thread_safe: Rc<u32>, _marker: PhantomData<*const ()>, } // Rc<u32> nie wdraża Send/Sync, więc MyType także nie wdroży tych traitów.
Jeśli wdrażasz niskopoziomowy wrapper i ręczne zarządzanie bezpieczeństwem wątków, można wdrożyć Send/Sync ręcznie (unsafe):
unsafe impl Send for MyType {} unsafe impl Sync for MyType {}
To odpowiedzialność programisty — dać gwarancje bezpieczeństwa wątków.
Co się stanie, jeśli wyciągniesz Rc<T> do wielu wątków przez Arc<Rc<T>>?
Często myśli się, że Arc wszystko chroni. Ale Rc<T> nie wdraża Send/Sync, nawet jeśli jest owinięte w Arc! Oto jak:
use std::rc::Rc; use std::sync::Arc; fn main() { let data = Arc::new(Rc::new(5)); // std::thread::spawn(move || { // println!("{:?}", data); // }); // Kompilator nie pozwoli na to! }
Arc nie kompensuje braku Send/Sync u wewnętrznych członków.
Historia
W projekcie była próba użycia Arc<Rc<T>>, aby podzielić dane między wątkami i nieblokująco dzielić własność. Program zawieszał się podczas wykonywania z nieprzewidywalnym zachowaniem; okazało się, że Rc nie jest bezpieczne dla wątków, a początkowo brakowało znajomości traitów Send/Sync.
Historia
W wykonanym przez siebie pętli zdarzeń typ State przechowywał surowy wskaźnik do danych. Typ State oznaczono jako unsafe impl Send, ale zapomniano o synchronizacji. W rezultacie wystąpił klasyczny wyścig danych, ujawniony już po wydaniu.
Historia
Programista wdrożył newtype wrapper nad Mutex, ale omyłkowo uczynił go !Sync, nie wdrażając Sync ręcznie. To uniemożliwiło użycie typu w kontekście wielowątkowym (na przykład, wewnątrz Arc<Mutex<T>>), ponieważ kompilator wymagał Sync. Naprawiono wdrażając unsafe impl Sync i analizując bezpieczeństwo.