面试题答案
一键面试在Rust中,std::sync::atomic
模块提供了原子类型,可用于避免数据竞争,并且原子操作也可以对内存顺序进行控制,一定程度上防止重排现象。
- 检测数据竞争:
- 数据竞争通常发生在多个线程同时访问共享可变数据,且至少有一个是写操作,并且没有适当的同步机制时。
std::sync::atomic
中的原子类型通过提供原子操作,使得对这些类型的读写操作是线程安全的,从而避免数据竞争。 - 例如,
AtomicUsize
类型可以安全地在多线程环境下读写:
- 数据竞争通常发生在多个线程同时访问共享可变数据,且至少有一个是写操作,并且没有适当的同步机制时。
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
fn main() {
let counter = AtomicUsize::new(0);
let mut handles = vec![];
for _ in 0..10 {
let counter = counter.clone();
let handle = thread::spawn(move || {
counter.fetch_add(1, Ordering::SeqCst);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
assert_eq!(counter.load(Ordering::SeqCst), 10);
}
在这个例子中,多个线程同时对AtomicUsize
类型的counter
进行fetch_add
操作。由于fetch_add
是原子操作,不会出现数据竞争。
- 检测重排现象:
- 现代CPU和编译器为了优化性能,可能会对指令进行重排。原子操作的内存顺序参数可以控制这种重排。
- 例如,
Ordering::SeqCst
(顺序一致性)内存顺序会阻止编译器和CPU对原子操作进行重排,以保证所有线程看到的操作顺序一致。
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::thread;
fn main() {
let ready = AtomicBool::new(false);
let data = AtomicUsize::new(0);
let handle = thread::spawn(move || {
data.store(42, Ordering::SeqCst);
ready.store(true, Ordering::SeqCst);
});
while!ready.load(Ordering::SeqCst) {
// 等待
}
assert_eq!(data.load(Ordering::SeqCst), 42);
handle.join().unwrap();
}
在这个例子中,使用Ordering::SeqCst
内存顺序,确保data.store(42, Ordering::SeqCst)
操作在ready.store(true, Ordering::SeqCst)
之前完成,并且主线程在ready.load(Ordering::SeqCst)
为true
后读取data
时,能看到正确的值42
,防止了可能的指令重排导致的错误。