数据竞争和竞争条件(Data Races and Race Conditions)

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

  • 两个或多个线程同时访问内存位置

  • 其中一个或多个是写

  • 其中一个或多个是不同步的

数据竞争具有未定义行为,因此无法在Safe Rust中执行.数据竞争 主要(mostly) 通过Rust的所有权系统来防止:不可能为可变引用添加别名,因此无法执行数据竞争.内部可变性使这更加复杂,这很大程度上是我们拥有Send和Sync trait的原因(见下文).

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

这基本上是不可能的,而且可能真的不合需要.你的硬件很活泼,你的操作系统(OS)很活泼,你电脑上的其他程序都很活泼,而这一切都是充满活力的世界.任何可以真正声称可以防止 所有(all) 竞争条件的系统都会非常糟糕,如果不是不正确的话.

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

  1. use std::thread;
  2. use std::sync::atomic::{AtomicUsize, Ordering};
  3. use std::sync::Arc;
  4. let data = vec![1, 2, 3, 4];
  5. // Arc so that the memory the AtomicUsize is stored in still exists for
  6. // the other thread to increment, even if we completely finish executing
  7. // before it. Rust won't compile the program without it, because of the
  8. // lifetime requirements of thread::spawn!
  9. let idx = Arc::new(AtomicUsize::new(0));
  10. let other_idx = idx.clone();
  11. // `move` captures other_idx by-value, moving it into this thread
  12. thread::spawn(move || {
  13. // It's ok to mutate idx because this value
  14. // is an atomic, so it can't cause a Data Race.
  15. other_idx.fetch_add(10, Ordering::SeqCst);
  16. });
  17. // Index with the value loaded from the atomic. This is safe because we
  18. // read the atomic memory only once, and then pass a copy of that value
  19. // to the Vec's indexing implementation. This indexing will be correctly
  20. // bounds checked, and there's no chance of the value getting changed
  21. // in the middle. However our program may panic if the thread we spawned
  22. // managed to increment before this ran. A race condition because correct
  23. // program execution (panicking is rarely correct) depends on order of
  24. // thread execution.
  25. println!("{}", data[idx.load(Ordering::SeqCst)]);
  26. use std::thread;
  27. use std::sync::atomic::{AtomicUsize, Ordering};
  28. use std::sync::Arc;
  29. let data = vec![1, 2, 3, 4];
  30. let idx = Arc::new(AtomicUsize::new(0));
  31. let other_idx = idx.clone();
  32. // `move` captures other_idx by-value, moving it into this thread
  33. thread::spawn(move || {
  34. // It's ok to mutate idx because this value
  35. // is an atomic, so it can't cause a Data Race.
  36. other_idx.fetch_add(10, Ordering::SeqCst);
  37. });
  38. if idx.load(Ordering::SeqCst) < data.len() {
  39. unsafe {
  40. // Incorrectly loading the idx after we did the bounds check.
  41. // It could have changed. This is a race condition, *and dangerous*
  42. // because we decided to do `get_unchecked`, which is `unsafe`.
  43. println!("{}", data.get_unchecked(idx.load(Ordering::SeqCst)));
  44. }
  45. }