Rust
Unsichere Richtlinien
Suche…
Einführung
Erklären Sie, warum bestimmte Dinge in Rust als unsafe
werden und warum wir in bestimmten (seltenen) Situationen möglicherweise diese Notluke verwenden müssen.
Datenrennen
Datenrennen treten auf, wenn ein Teil des Speichers von einer Partei aktualisiert wird, während eine andere versucht, sie gleichzeitig zu lesen oder zu aktualisieren (ohne Synchronisation zwischen den beiden). Schauen wir uns das klassische Beispiel eines Datenrennens mit einem gemeinsamen Zähler an.
use std::cell::UnsafeCell;
use std::sync::Arc;
use std::thread;
// `UnsafeCell` is a zero-cost wrapper which informs the compiler that "what it
// contains might be shared mutably." This is used only for static analysis, and
// gets optimized away in release builds.
struct RacyUsize(UnsafeCell<usize>);
// Since UnsafeCell is not thread-safe, the compiler will not auto-impl Sync for
// any type containig it. And manually impl-ing Sync is "unsafe".
unsafe impl Sync for RacyUsize {}
impl RacyUsize {
fn new(v: usize) -> RacyUsize {
RacyUsize(UnsafeCell::new(v))
}
fn get(&self) -> usize {
// UnsafeCell::get() returns a raw pointer to the value it contains
// Dereferencing a raw pointer is also "unsafe"
unsafe { *self.0.get() }
}
fn set(&self, v: usize) { // note: `&self` and not `&mut self`
unsafe { *self.0.get() = v }
}
}
fn main() {
let racy_num = Arc::new(RacyUsize::new(0));
let mut handlers = vec![];
for _ in 0..10 {
let racy_num = racy_num.clone();
handlers.push(thread::spawn(move || {
for i in 0..1000 {
if i % 200 == 0 {
// give up the time slice to scheduler
thread::yield_now();
// this is needed to interleave the threads so as to observe
// data race, otherwise the threads will most likely be
// scheduled one after another.
}
// increment by one
racy_num.set(racy_num.get() + 1);
}
}));
}
for th in handlers {
th.join().unwrap();
}
println!("{}", racy_num.get());
}
Die Ausgabe ist fast immer unter 10000
(10 Threads × 1000), wenn sie auf einem Multi-Core-Prozessor ausgeführt wird.
In diesem Beispiel hat ein Datenrennen einen logisch falschen, aber dennoch sinnvollen Wert erzeugt. Dies liegt daran, dass nur ein einziges Wort in das Rennen involviert war und das Update daher nicht teilweise geändert werden konnte. Datenrennen können jedoch im Allgemeinen beschädigte Werte erzeugen, die für einen Typ (Typ unsicher) ungültig sind, wenn das Objekt, für das ein Rennen ausgeführt wird, mehrere Wörter umfasst, und / oder Werte erzeugt, die auf ungültige Speicherstellen (unsicherer Speicher) verweisen, wenn Zeiger beteiligt sind.
Die sorgfältige Verwendung atomarer Grundelemente kann jedoch den Aufbau sehr effizienter Datenstrukturen ermöglichen, die intern einige dieser "unsicheren" Vorgänge ausführen müssen, um Aktionen auszuführen, die nicht statisch durch das Rust-Typensystem überprüfbar sind, aber insgesamt korrekt sind (dh einen Safe erstellen) Abstraktion).