Historisch gezien ging de samenwerking met multithreading vaak gepaard met crashes, racecondities en geheugenlekken, vooral bij ongecontroleerde geheugenuitwisseling. Rust implementeert het concept van threadveiligheid op het niveau van types — een object kan alleen naar een thread worden overgedragen als het de benodigde traits (Send, Sync) implementeert. De threads zelf worden gemaakt via std::thread::spawn, en de communicatie tussen hen gebeurt via kanalen of gedeeld geheugen met gecontroleerde mutatie (Mutex, Arc).
Probleem: Handmatig synchroniseren is moeilijk en gevaarlijk. Het overdragen van willekeurige objecten tussen threads zonder expliciete eigendomsoverdracht leidt tot racecondities en crashes.
Oplossing: Alleen expliciet verplaatsbare (move) objecten of gedeeld via Arc, Mutex, evenals ingebouwde berichtenkanalen (std::sync::mpsc, crossbeam). Dit minimaliseert fouten die verband houden met synchroniseren en asynchroon gegevensverkeer: eigenaarschap is altijd eenduidig.
Codevoorbeeld:
use std::thread; use std::sync::mpsc; fn main() { let (tx, rx) = mpsc::channel(); thread::spawn(move || { tx.send(String::from("Hallo van de thread!")).unwrap(); }); let received = rx.recv().unwrap(); println!("Ontvangen: {}", ontvangen); }
Belangrijkste kenmerken:
Kan ik na het verplaatsen van een object via move het nog gebruiken in de hoofdthread?
Nee, zodra een object is verplaatst (bijvoorbeeld in een closure in thread::spawn), kan het niet meer in de ouderthread worden gebruikt; de compiler zal de code niet laten compileren.
Kan ik mutabele referenties (&mut T) tussen threads doorgeven?
Nee, een mutabele referentie &mut T kan maar in één instantie bestaan, en trait Send is hier standaard niet op geïmplementeerd. Voor het werken met veranderlijke gegevens wordt een wrapper via Mutex/Arc gebruikt.
Waarom kan ik Rc<T> niet gebruiken voor gedeeld eigenaarschap tussen threads?
Rc<T> implementeert geen Sync en Send, omdat zijn interne teller niet threadveilig is. Voor thread-safe gebruik wordt Arc<T> gebruikt (atomische referentieteller).
// Vergelijking tussen Rc en Arc use std::sync::Arc; let x = Arc::new(5); // kan worden gekloond en gedeeld tussen threads
Een ontwikkelaar besloot een string tussen threads te delen met Rc<String>, plaatst Rc binnen thread::spawn. De code compileert alleen als het geforceerd wordt gecast via unsafe, waarna de applicatie kan crashen of met beschadigde gegevens kan werken.
Voordelen:
Nadelen:
Er wordt Arc<String> + Mutex<String> gebruikt voor veilige toegang, of een bericht wordt via een kanaal verzonden zonder gedeeld eigenaarschap.
Voordelen:
Nadelen: