Ch 30: std::sync::Mutex - 互斥锁
std::sync::Mutex(互斥锁)提供了一种方式来保护共享数据,确保同一时间只有一个线程可以访问。Rust的Mutex是 PoisonError感知的,即如果持有锁的线程panic,锁会自动”中毒”。
2. 基本用法
Section titled “2. 基本用法”use std::sync::Mutex;
fn main() { let m = Mutex::new(5);
{ // lock()返回Result<Guard<T>> let mut num = m.lock().unwrap(); *num = 10; println!("Mutex中的值: {}", *num); } // lock_guard在这里被drop,锁释放
println!("解锁后的值: {}", *m.lock().unwrap());}3. 方法签名
Section titled “3. 方法签名”impl<T> Mutex<T> { pub fn new(t: T) -> Mutex<T> pub fn lock(&self) -> Result<MutexGuard<T>, PoisonError<MutexGuard<T>>> pub fn try_lock(&self) -> Result<MutexGuard<T>, TryLockError>}
impl<T> Mutex<T> { pub fn get_mut(&self) -> Result<MutGuard<T>, PoisonError<MutGuard<T>>> pub fn into_inner(self) -> Result<T, PoisonError<Self>>}4. lock vs try_lock
Section titled “4. lock vs try_lock”use std::sync::Mutex;use std::thread;
fn main() { let m = Mutex::new(0i32);
// try_lock立即返回,不会阻塞 let result = m.try_lock(); match result { Ok(_guard) => println!("获取锁成功"), Err(_) => println!("锁已被占用"), }
// 持有锁 drop(result);
let handle = thread::spawn(move || { let _guard = m.lock().unwrap(); println!("子线程获取了锁"); });
// 主线程再次尝试try_lock match m.try_lock() { Ok(_guard) => println!("主线程获取了锁"), Err(_) => println!("主线程获取锁失败,锁被占用"), }
handle.join().unwrap();}5. MutexGuard - 锁守卫
Section titled “5. MutexGuard - 锁守卫”MutexGuard是一个智能指针,在drop时自动释放锁:
use std::sync::Mutex;use std::sync::MutexGuard;
fn main() { let m = Mutex::new(vec![1, 2, 3]);
// 显式使用Guard let guard: MutexGuard<Vec<i32>> = m.lock().unwrap();
// guard实现了Deref,可以像引用一样使用 println!("长度: {}", guard.len()); println!("第一个: {}", guard[0]);
// 手动释放 drop(guard); println!("锁已释放");}Guard方法:
impl<'a, T> MutexGuard<'a, T> { pub fn downgrade(this: MutexGuard<'a, T>) -> MutexGuard<'a, T>}
impl<T> Deref for MutexGuard<T> { type Target = T; fn deref(&self) -> &T;}
impl<T> DerefMut for MutexGuard<T> { fn deref_mut(&mut self) -> &mut T;}6. PoisonError - 锁中毒
Section titled “6. PoisonError - 锁中毒”当持有锁的线程panic时,锁会”中毒”:
use std::sync::Mutex;use std::thread;
fn main() { let m = Mutex::new(5i32);
let handle = thread::spawn(move || { let mut _guard = m.lock().unwrap(); panic!("子线程panic了!"); });
// 主线程尝试获取锁 let result = handle.join();
// lock会返回PoisonError match m.lock() { Ok(_guard) => println!("获取锁成功"), Err(poisoned) => { println!("锁已中毒"); // 从PoisonError恢复数据 let guard = poisoned.into_inner(); println!("数据值: {}", guard); } }}7. Arc与Mutex配合
Section titled “7. Arc与Mutex配合”Arc(原子引用计数)用于跨线程共享Mutex:
use std::sync::{Arc, Mutex};use std::thread;
fn main() { let counter = Arc::new(Mutex::new(0i32)); let mut handles = vec![];
for _ in 0..10 { let counter = Arc::clone(&counter); let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); }
for handle in handles { handle.join().unwrap(); }
println!("最终计数: {}", *counter.lock().unwrap());}8. 死锁避免
Section titled “8. 死锁避免”use std::sync::Mutex;
fn main() { let a = Mutex::new(1i32); let b = Mutex::new(2i32);
// 始终按固定顺序获取锁 let (first, second) = if a.lock().unwrap() < b.lock().unwrap() { (a, b) } else { (b, a) };
let mut _first = first.lock().unwrap(); let mut _second = second.lock().unwrap();
println!("避免死锁");}9. 注意事项
Section titled “9. 注意事项”- 锁粒度:锁的粒度要适当,太粗降低并发,太细增加开销
- 不要在持有锁时执行耗时操作:这会阻塞其他线程
- 避免死锁:始终按相同顺序获取多个锁
- PoisonError:可以使用
into_inner()恢复数据 - Guard生命周期:Guard被drop时自动释放锁
10. 总结
Section titled “10. 总结”std::sync::Mutex核心API:
Mutex::new()- 创建互斥锁lock()- 获取锁(阻塞)try_lock()- 尝试获取锁(非阻塞)PoisonError- 锁中毒错误处理MutexGuard- 自动释放的锁守卫
Mutex + Arc是Rust中最常用的线程安全共享状态模式:
let data = Arc::new(Mutex::new(初始值));// 跨线程clone Arc