面试题答案
一键面试原子操作和内存顺序的选择及理由
在Rust中,对于AtomicU64
类型,要确保读取线程能准确获取到递增后的值,递增线程可以使用fetch_add
方法,并搭配Release
内存顺序,读取线程使用load
方法并搭配Acquire
内存顺序。
Release
内存顺序保证在这个原子操作之前的所有写操作对其他持有Acquire
或SeqCst
内存顺序的线程可见。Acquire
内存顺序保证在这个原子操作之后的所有读操作能看到之前被标记为Release
或SeqCst
内存顺序的写操作。
代码示例
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicU64, Ordering};
use std::thread;
fn main() {
let counter = Arc::new(AtomicU64::new(0));
let counter_clone = counter.clone();
let increment_thread = thread::spawn(move || {
for _ in 0..1000 {
counter_clone.fetch_add(1, Ordering::Release);
}
});
let read_thread = thread::spawn(move || {
for _ in 0..1000 {
let value = counter.load(Ordering::Acquire);
println!("Read value: {}", value);
}
});
increment_thread.join().unwrap();
read_thread.join().unwrap();
}
错误使用宽松内存顺序的问题
如果使用过于宽松的内存顺序,比如在递增线程中使用Relaxed
内存顺序,可能会导致读取线程看不到递增线程所做的更新。这是因为Relaxed
内存顺序不提供任何跨线程的内存可见性保证,它只保证原子操作本身的原子性,而不保证操作的顺序与其他线程的操作顺序之间的关系。所以读取线程可能会读取到旧的值,而不是递增后的值。