Historycznie praca z wielowątkowością była związana z awariami, wyścigami i wyciekami, zwłaszcza przy niekontrolowanej wymianie pamięci. Rust wdraża koncepcję bezpieczeństwa wątków na poziomie typów — obiekt można przesłać do wątku tylko wtedy, gdy realizuje wymagane traity (Send, Sync). Same wątki są tworzone za pomocą std::thread::spawn, a komunikacja między nimi odbywa się za pośrednictwem kanałów lub pamięci współdzielonej z kontrolowaną mutacją (Mutex, Arc).
Problem: ręczne zarządzanie synchronizacją jest trudne i niebezpieczne. Przesyłanie dowolnych obiektów między wątkami bez wyraźnego przekazania własności prowadzi do wyścigów i awarii.
Rozwiązanie: tylko jawnie przenoszone (move) obiekty lub współdzielone przez Arc, Mutex oraz wbudowane kanały wiadomości (std::sync::mpsc, crossbeam). To minimalizuje błędy związane z synchronizacyjną i asynchronizacyjną wymianą danych: własność zawsze jest jednoznaczna.
Przykład kodu:
use std::thread; use std::sync::mpsc; fn main() { let (tx, rx) = mpsc::channel(); thread::spawn(move || { tx.send(String::from("Witaj z wątku!")).unwrap(); }); let received = rx.recv().unwrap(); println!("Odebrano: {}", received); }
Kluczowe cechy:
Czy po przesłaniu obiektu przez move można dalej go używać w głównym wątku?
Nie, jak tylko obiekt został przeniesiony (na przykład do closure w thread::spawn), używanie go w wątku macierzystym jest niemożliwe, kompilator nie pozwoli na zbudowanie kodu.
Czy można przesyłać mutowalne referencje (&mut T) między wątkami?
Nie, mutowalna referencja &mut T może istnieć tylko w jednym egzemplarzu, a trait Send nie jest na nią realizowany domyślnie. Do pracy z danymi zmiennymi używa się opakowania za pomocą Mutex/Arc.
Dlaczego nie można używać Rc<T> do podziału własności między wątkami?
Rc<T> nie realizuje Sync i Send, ponieważ jego wewnętrzny licznik nie jest bezpieczny dla wątków. Do użytku w bezpiecznych wątkach używa się Arc<T> (atomic reference counter).
// Porównanie Rc i Arc use std::sync::Arc; let x = Arc::new(5); // można klonować i dzielić między wątkami
Programista postanowił podzielić ciąg między wątkami przy użyciu Rc<String>, umieszcza Rc wewnątrz thread::spawn. Kod kompiluje się tylko wtedy, gdy jest forcowano z użyciem unsafe, po czym aplikacja może się wykrzacza lub działać z uszkodzonymi danymi.
Zalety:
Wady:
Używa Arc<String> + Mutex<String> do zabezpieczonego dostępu, lub przesyła wiadomość przez kanał, bez wspólnej własności.
Zalety:
Wady: