共享状态:Arc 与 Mutex
你会学到什么
Arc<T>是线程安全的引用计数(Rc的多线程版)。Mutex<T>用锁保证同一时刻只有一个线程能改数据。Arc<Mutex<T>>是多线程共享可变状态的标准组合。
最小示例
use std::sync::{Arc, Mutex};
let counter = Arc::new(Mutex::new(0));
*counter.lock().unwrap() += 1;
运行代码
cd examples
cargo run -p rt_27_shared_state
cargo test -p rt_27_shared_state
代码讲解
每个线程拿到 Arc 的克隆,lock() 取得独占访问:
let counter = Arc::clone(&counter);
thread::spawn(move || {
let mut value = counter.lock().unwrap();
*value += 1;
});
lock() 返回一个 MutexGuard,离开作用域时自动解锁。Arc 提供共享所有权且线程安全(Rc 不是),Mutex 提供互斥访问——两者缺一不可。
编译器通过 Send / Sync 这两个 marker trait 在编译期就拦住不安全的跨线程共享。
常见错误
用 Rc 而不是 Arc 跨线程:
let counter = Rc::new(Mutex::new(0));
thread::spawn(move || { /* ❌ Rc 不是 Send */ });
跨线程共享所有权必须用 Arc。
练习
- 用
Arc<Mutex<Vec<i32>>>让多个线程往同一个Vec里 push。 - 把示例改成
Arc<Mutex<HashMap<String, usize>>>的并发计数。
小结
Arc 管共享,Mutex 管互斥。Arc<Mutex<T>> 让多线程安全地读写同一份数据,违规会被编译期 Send/Sync 拦截。
下一步
代码变多后需要组织模块。下一章进入模块系统与 crate。
完整示例代码
下面是 examples/27_shared_state/ 的完整源码。无需 clone 仓库,直接在页面上阅读、复制、对照运行。
examples/27_shared_state/src/main.rs
//! 用 Arc<Mutex<T>> 在多个线程间共享可变状态。
use std::sync::{Arc, Mutex};
use std::thread;
/// 多个线程各自累加同一个计数器。
fn concurrent_count(threads: usize, per_thread: usize) -> usize {
let counter = Arc::new(Mutex::new(0usize));
let handles: Vec<_> = (0..threads)
.map(|_| {
let counter = Arc::clone(&counter);
thread::spawn(move || {
for _ in 0..per_thread {
let mut value = counter.lock().expect("锁被毒化");
*value += 1;
}
})
})
.collect();
for handle in handles {
handle.join().expect("线程 panic");
}
let total = *counter.lock().expect("锁被毒化");
total
}
fn main() {
let total = concurrent_count(4, 1000);
println!("total = {total}");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn counts_without_races() {
assert_eq!(concurrent_count(4, 1000), 4000);
}
#[test]
fn zero_threads() {
assert_eq!(concurrent_count(0, 100), 0);
}
} examples/27_shared_state/Cargo.toml
[package]
name = "rt_27_shared_state"
version.workspace = true
edition.workspace = true
publish.workspace = true