面试题答案
一键面试设计思路
- 选择合适的原子类型:在Rust中,
std::sync::atomic
模块提供了一系列原子类型,如AtomicUsize
、AtomicI32
等,根据共享资源的类型选择对应的原子类型。如果共享资源是复杂类型,可以考虑使用AtomicPtr
结合Box
等方式来处理。 - 避免不必要的锁:原子操作不需要像
Mutex
或RwLock
那样获取锁,直接对原子类型进行操作,减少线程间的锁竞争。例如,对于简单的计数器,可以直接使用AtomicUsize
的fetch_add
等方法进行原子更新。 - 使用
Arc
共享原子数据:为了在多个线程间共享原子类型的数据,使用Arc
(原子引用计数)来包装原子类型。Arc
可以在多线程环境下安全地共享数据,并且其内部的引用计数操作也是原子的。
关键代码示例
use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};
use std::thread;
fn main() {
// 创建一个原子计数器
let counter = Arc::new(AtomicUsize::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter_clone = Arc::clone(&counter);
let handle = thread::spawn(move || {
// 每个线程对计数器进行1000次原子增加操作
for _ in 0..1000 {
counter_clone.fetch_add(1, Ordering::SeqCst);
}
});
handles.push(handle);
}
// 等待所有线程完成
for handle in handles {
handle.join().unwrap();
}
// 输出最终的计数值
println!("Final counter value: {}", counter.load(Ordering::SeqCst));
}
在上述代码中:
- 首先创建了一个
AtomicUsize
类型的原子计数器,并使用Arc
进行包装,以便在多个线程间共享。 - 然后通过
thread::spawn
创建10个线程,每个线程对计数器进行1000次原子增加操作。这里使用fetch_add
方法,该方法以原子方式增加计数器的值,并返回旧值。Ordering::SeqCst
表示顺序一致性,这是一种较为严格的内存序,确保所有线程以相同的顺序看到对该原子变量的修改。 - 最后等待所有线程完成,并输出最终的计数值。