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 有两个特点:
- 必须要先获取锁,才能通过解引用访问内部数据。这保证了你如果不加锁,根本拿不到数据(编译器按头让你加锁)。
- 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}
注意 counter 在 thread::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 是面向对象的吗?它支持继承吗?怎么实现多态?
练习题:
- 尝试把
Arc换成Rc,看看编译器会报什么错(提示:TraitSendis not implemented)。 - 使用
Mutex和thread模拟著名的 “哲学家就餐问题”(注意避免死锁)。
思考题:
Rust 的 Mutex 在 lock() 失败(比如因为 PoisonError)时会返回 Result。什么是 PoisonError?为什么 Rust 要设计这种机制?
本文代码示例:
关注公众号:极客老墨
更多 AI 应用开发、工程实践和效率工具分享,欢迎扫码关注。
