Copyright © 2022-2024 aizws.net · 网站版本: v1.2.6·内部版本: v1.23.3·
页面加载耗时 0.00 毫秒·物理内存 59.0MB ·虚拟内存 1300.0MB
欢迎来到 AI 中文社区(简称 AI 中文社),这里是学习交流 AI 人工智能技术的中文社区。 为了更好的体验,本站推荐使用 Chrome 浏览器。
安全Rust保证了不存在数据竞争。数据竞争指的是:
数据竞争导致未定义行为,所以不可能在安全Rust中存在。大多数情况下,Rust的所有权系统就可以避免数据竞争:不可能有可变引用的别名,因此也就不可能有数据竞争。但是内部可变性把这件事弄得复杂了,这也是为什么我们要有Send和Sync(见下)。
但是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,这样即使程序已经执行完毕了,存储AtomicUsize的内存依然存在, // 其他的线程可以增加它的值。否则Rust不能编译这段代码,因为thread:spawn // 对生命周期有限制。 let idx = Arc::new(AtomicUsize::new(0)); let other_idx = idx.clone(); // move获得other_idx的所有权,将它移入线程 thread::spawn(move || { // 可以改变idx,因为它的值是一个原子,不会引起数据竞争 other_idx.fetch_add(10, Ordering::SeqCst); }); // 用原子中的值做索引。这么做是安全的,因为我们只读取了一次原子的内存, // 然后将读出的值的拷贝传递给Vec做索引。索引过程可以做正确的边界检查, // 在执行索引期间这个值也不会发生改变。 // 但是,如果上面的线程在执行这句代码之前增加了这个值,这段代码会panic。 // 这符合竞争条件,因为程序执行得正确与否(panic几乎不可能是正确的) // 依赖于线程的执行顺序 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获得other_idx的所有权,将它移入线程 thread::spawn(move || { // 可以改变idx,因为它的值是一个原子,不会引起数据竞争 other_idx.fetch_add(10, Ordering::SeqCst); }); if idx.load(Ordering::SeqCst) < data.len() { unsafe { // 在边界检查之后读取idx的值是不正确的,因为它有可能已经改变了。 // 这是一个竞争条件,而且十分危险,因为我们要使用的get_unchecked是非安全的。 println!("{}", data.get_unchecked(idx.load(Ordering::SeqCst))); } }
不是所有类型都遵守可变性的原则。有一些类型允许你拥有一块内存的多个别名,同时还改变内存的值。除非这些类型使用同步来控制访问,否则它们就不是线程安全的。Rust根据Send和Sync这两个trait获取相关信息。 ...