Rust 学习笔记 22:共享状态并发 (Shared-State Concurrency)

“Do not communicate by sharing memory; instead, share memory by communicating.” – Go Language Slogan

虽然 Go 倡导通过通信来共享内存(Channel),但 Rust 同样提供了强大的共享状态并发工具。而且得益于所有权系统,Rust 中的锁是线程安全的(Thread Safety),你很难写出有数据竞争的代码。

1. Mutex (互斥锁)

Mutex (Mutual Exclusion) 让同一时刻只有一个线程访问数据。 Rust 的 Mutex 有两个特点:

  1. 必须要先获取锁,才能通过解引用访问内部数据。这保证了你如果不加锁,根本拿不到数据(编译器按头让你加锁)。
  2. RAII:锁的释放在 Guard 离开作用域时自动发生,不需要手动 unlock
1let m = Mutex::new(5);
2{
3    let mut num = m.lock().unwrap(); // 获取锁,num 是 MutexGuard
4    *num = 6; // 修改数据
5} // 离开作用域,锁自动释放

2. 原子引用计数 Arc

要在多个线程间共享 Mutex,直接传是不行的(所有权规则)。 你可能会想到 Rc<T>,但 Rc 不是线程安全的。 我们需要 Arc<T> (Atomic Reference Counting)。

 1use std::sync::{Arc, Mutex};
 2use std::thread;
 3
 4let counter = Arc::new(Mutex::new(0));
 5let mut handles = vec![];
 6
 7for _ in 0..10 {
 8    let counter = Arc::clone(&counter); // 增加引用计数
 9    let handle = thread::spawn(move || {
10        let mut num = counter.lock().unwrap();
11        *num += 1;
12    });
13    handles.push(handle);
14}

注意 counterthread::spawn 前被 clone 了一份,然后 move 进了闭包。这样每个线程都拥有 Mutex 的一个所有权份额。

3. Send 和 Sync Traits

Rust 语言本身对并发知之甚少,几乎所有并发特性(Thread, Mutex, Arc)都是标准库提供的。 Rust 只有两个内建的并发相关 Trait:

  • Send:表明类型的所有权可以在线程间传递。几乎所有基本类型都是 Send 的(除了 Rc 等)。
  • Sync:表明类型的引用 &T 可以在线程间传递。如果 T 是 Sync 的,意味着 &T 是 Send 的。

Mutex<T> 是 Sync 的,所以它可以被多线程访问。Rc<T> 不是 Send 也不是 Sync 的,所以编译器会禁止你在线程间用 Rc

4. 小结

第22篇笔记。

  • Mutex:提供互斥访问。
  • Arc:提供线程安全的多重所有权。
  • Arc<Mutex>:多线程共享可变状态的黄金搭档。

下一篇,我们将探讨 Rust 的 面向对象特性 (Object Oriented Features)。Rust 是面向对象的吗?它支持继承吗?怎么实现多态?


练习题

  1. 尝试把 Arc 换成 Rc,看看编译器会报什么错(提示:Trait Send is not implemented)。
  2. 使用 Mutexthread 模拟著名的 “哲学家就餐问题”(注意避免死锁)。

思考题

Rust 的 Mutexlock() 失败(比如因为 PoisonError)时会返回 Result。什么是 PoisonError?为什么 Rust 要设计这种机制?


本文代码示例

关注公众号:极客老墨

更多 AI 应用开发、工程实践和效率工具分享,欢迎扫码关注。

极客老墨微信公众号二维码

相关阅读