4. 原子操作实现计数器
任务说明
实现一个多线程环境下的安全计数器。多个线程同时对同一个计数器执行递增操作,要求最终结果与线程数量、每个线程递增次数严格一致,且全程无数据竞争。使用 AtomicUsize 配合 std::thread 完成,不借助 Mutex 或 RwLock。
核心实现
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
fn main() {
// 目标:8个线程,每个线程递增10000次
const THREADS: usize = 8;
const INCREMENTS_PER_THREAD: usize = 10_000;
// 原子计数器,初始值为0
let counter = AtomicUsize::new(0);
// 创建线程句柄容器
let mut handles = Vec::with_capacity(THREADS);
for _ in 0..THREADS {
// 每个线程持有一个 AtomicUsize 的引用
let counter_ref = &counter;
handles.push(thread::spawn(move || {
for _ in 0..INCREMENTS_PER_THREAD {
// Relaxed 即可满足计数需求(无跨线程顺序约束)
counter_ref.fetch_add(1, Ordering::Relaxed);
}
}));
}
// 等待所有线程完成
for handle in handles {
handle.join().expect("线程执行失败");
}
// 读取最终值
let final_value = counter.load(Ordering::Relaxed);
let expected = THREADS * INCREMENTS_PER_THREAD;
println!("最终计数: {}", final_value);
println!("期望值: {}", expected);
assert_eq!(final_value, expected, "计数结果与期望不符,存在数据竞争");
println!("验证通过:无数据竞争,计数准确");
}
关键设计说明
- 原子类型:
AtomicUsize保证fetch_add的读-改-写操作在硬件层面不可分割,无需加锁。 - 内存序:此处仅需计数准确,无跨线程数据依赖,使用
Ordering::Relaxed即可,开销最低。 - 所有权与借用:通过
&counter将原子变量的引用移入闭包,多个线程共享同一计数器。 - 线程同步:
join()确保所有线程结束后再读取最终值,避免读取过早。
编译与运行
# 创建项目(若已有 Cargo.toml 可跳过)
cargo new atomic_counter
cd atomic_counter
# 将上述代码写入 src/main.rs
# 编译并运行
cargo run
验证方法与判定标准
程序运行后,控制台输出两行数值。判定通过需同时满足:
- 输出行
最终计数: 80000与期望值: 80000数值完全一致; - 程序正常退出,无 panic,且打印
验证通过:无数据竞争,计数准确。
若改用普通 usize 而非原子类型,多次运行大概率出现计数小于 80000 的情况,可自行替换对比验证原子操作的必要性。
预期输出
最终计数: 80000
期望值: 80000
验证通过:无数据竞争,计数准确