use std::sync::{Arc, Mutex};
use std::sync::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 || {
for _ in 0..1000 {
counter_clone.fetch_add(1, Ordering::SeqCst);
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Total count: {}", counter.load(Ordering::SeqCst));
}
原子操作对可扩展性的影响
- 减少锁争用:原子操作避免了传统锁机制下多个线程频繁争夺锁的情况。在上述代码中,使用
AtomicUsize
的fetch_add
方法,每个线程可以直接对计数器进行原子递增,无需获取锁,从而提高了并行度,在多核处理器环境下能更好地利用资源,增强可扩展性。
- 内存一致性:原子操作通过
Ordering
参数来保证内存一致性。例如Ordering::SeqCst
提供了顺序一致性,确保所有线程以相同顺序看到原子操作的结果。虽然这种强一致性在某些情况下会有性能开销,但对于计数器这种需要准确统计结果的场景是必要的,并且相较于锁机制,原子操作在维持一致性的同时,对可扩展性的影响相对较小。不过,如果使用较弱的内存顺序(如Ordering::Relaxed
),虽然可能在性能上有所提升,但可能导致在多线程环境下数据不一致,影响程序正确性,所以在保证可扩展性时要平衡好一致性和性能的关系。