数据竞争与竞争条件

原文跟踪races.md   Commit: bb75621e3a62d14302c8f09239aab42132530446

Safe Rust保证不存在数据争用,其定义为:

  • 两个或多个线程同时访问内存位置
  • 其中一个是写
  • 其中一个是不同步的

数据争用具有未定义的行为,因此无法在Safe Rust执行。 通过Rust的所有权系统阻止了大部分数据竞争:不可能为可变引用添加别名,因此无法执行 数据竞争。 内部可变性使这更复杂,这很大程度上是原因我们有SendSync特质(见下文)。

但Rust并不能阻止一般的竞争条件

这在根本上是不可能的,也可能是不可取的。 您的硬件是活的,你的操作系统是活的,你的电脑上的其他程序是活的,这一切都是充满活力的世界。 任何可以真正声称防止所有竞争条件的系统使用起来非常糟糕。

因此,对于安全Rust程序来说,使用死锁或使用不正确的同步做一些无意义的事情是完全“好”的。 显然这样的程序不是很好,但Rust到目前为止只能为你做这些。 但是,竞争条件本身不能违反Rust程序中的内存安全性。 只有与其他一些不安全的代码一起使用,竞争条件才会真正违反内存安全。 例如:

use std::thread; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; let data = vec![1, 2, 3, 4]; // Arc so that the memory the AtomicUsize is stored in still exists for // the other thread to increment, even if we completely finish executing // before it. Rust won't compile the program without it, because of the // lifetime requirements of thread::spawn! let idx = Arc::new(AtomicUsize::new(0)); let other_idx = idx.clone(); // `move` captures other_idx by-value, moving it into this thread thread::spawn(move || { // It's ok to mutate idx because this value // is an atomic, so it can't cause a Data Race. other_idx.fetch_add(10, Ordering::SeqCst); }); // Index with the value loaded from the atomic. This is safe because we // read the atomic memory only once, and then pass a copy of that value // to the Vec's indexing implementation. This indexing will be correctly // bounds checked, and there's no chance of the value getting changed // in the middle. However our program may panic if the thread we spawned // managed to increment before this ran. A race condition because correct // program execution (panicking is rarely correct) depends on order of // thread execution. println!("{}", data[idx.load(Ordering::SeqCst)]);
use std::thread; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; let data = vec![1, 2, 3, 4]; let idx = Arc::new(AtomicUsize::new(0)); let other_idx = idx.clone(); // `move` captures other_idx by-value, moving it into this thread thread::spawn(move || { // It's ok to mutate idx because this value // is an atomic, so it can't cause a Data Race. other_idx.fetch_add(10, Ordering::SeqCst); }); if idx.load(Ordering::SeqCst) < data.len() { unsafe { // Incorrectly loading the idx after we did the bounds check. // It could have changed. This is a race condition, *and dangerous* // because we decided to do `get_unchecked`, which is `unsafe`. println!("{}", data.get_unchecked(idx.load(Ordering::SeqCst))); } }