并发与多线程 intermediate 30 分钟 更新 2026-06-15

共享状态:Arc 与 Mutex

用 Arc<Mutex<T>> 在多线程间安全共享可变数据。

共享状态: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