3. 共享状态与互斥锁
任务说明
本案例要求使用 Arc<Mutex<T>> 在多个线程间安全共享可变数据。我们将实现一个计数器累加程序:创建 4 个线程,每个线程对共享计数器执行 1000 次自增操作,最终验证计数器的值精确等于 4000。该案例直接对应 Rust 标准库中 Arc(原子引用计数)与 Mutex(互斥锁)的协作机制——Arc 使数据拥有多个所有者,Mutex 保证同一时刻仅一个线程能修改数据。
核心实现
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
// 创建共享计数器,包在 Mutex 中,再用 Arc 提供多线程共享
let counter = Arc::new(Mutex::new(0u32));
let mut handles = vec![];
for _ in 0..4 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
for _ in 0..1000 {
// 加锁获取可变引用,自增后锁自动释放
let mut num = counter.lock().unwrap();
*num += 1;
}
});
handles.push(handle);
}
// 等待所有线程完成
for handle in handles {
handle.join().unwrap();
}
// 读取最终值
let final_value = *counter.lock().unwrap();
println!("最终计数器值: {}", final_value);
}
关键机制说明
Arc::clone(&counter) 为每个线程创建共享所有权的句柄,线程通过 move 闭包捕获该句柄。counter.lock() 返回 MutexGuard,对内部数据的访问通过解引用完成;当 MutexGuard 离开作用域时锁自动释放,避免手动解锁遗漏。unwrap() 用于处理锁中毒(poisoning)异常——若某线程在持锁期间 panic,后续 lock() 会返回错误,此处简化处理直接展开。
编译与运行
// 保存为 main.rs,执行:
rustc main.rs
./main
也可使用 Cargo:cargo new mutex_demo && cd mutex_demo,将上述代码替换 src/main.rs 后执行 cargo run。
验证方法与判定标准
运行程序后观察终端输出。若输出为 最终计数器值: 4000,则证明 Arc<Mutex<T>> 正确实现了跨线程的互斥访问与数据共享。若去掉 Mutex 仅用 Arc<Cell<u32>> 或 Arc<RefCell<u32>> 则无法通过编译(因 Cell/RefCell 非 Sync),这从类型系统层面验证了 Mutex 的必要性。可尝试将自增次数改为不同值或增加线程数,最终结果始终等于 线程数 × 每线程次数,即为通过。
代码自检说明
上述代码完整实现了任务目标:Arc 负责多线程共享所有权,Mutex 保证每次自增操作的原子性。每个线程执行 1000 次 lock() + 自增 + 自动解锁,4 个线程并发执行后,最终值必然为 4000(无数据竞争)。若将 Mutex 替换为 RefCell,编译器会直接拒绝——因为 RefCell<u32> 未实现 Sync trait,无法跨线程共享,这从类型层面验证了方案的正确性。