面试题答案
一键面试场景描述
在多线程环境下,我们希望实现一个计数器,该计数器能够被多个线程安全地访问和修改,且不需要使用锁机制。使用比较交换操作(CAS)来实现此计数器,线程在尝试更新计数器值时,会先检查当前值是否与预期值相同,如果相同则更新为新值,否则重试。这样可以避免锁带来的性能开销,特别是在高并发场景下,多个线程竞争访问计数器时,CAS操作能够以更高效的方式完成计数器的更新。
主要Rust代码片段
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_clone = counter.clone();
let handle = thread::spawn(move || {
for _ in 0..100 {
loop {
let current = counter_clone.load(Ordering::Relaxed);
let new = current + 1;
if counter_clone.compare_and_swap(current, new, Ordering::Relaxed) == current {
break;
}
}
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Final counter value: {}", counter.load(Ordering::Relaxed));
}
在上述代码中:
AtomicUsize
是Rust标准库中提供的原子类型,用于在多线程环境下无锁地操作usize
类型的数据。AtomicUsize::new(0)
创建了一个初始值为0的原子计数器。- 在每个线程中,通过
loop
循环不断尝试使用compare_and_swap
方法更新计数器。compare_and_swap
方法接收当前预期值current
、新值new
以及内存序Ordering::Relaxed
。如果当前值与预期值相同,则将计数器更新为新值并返回旧值;如果不同,则返回当前实际值,此时线程会重新获取当前值并再次尝试更新。 - 最后,主线程等待所有子线程完成,并打印最终的计数器值。