Mutex<T>(互斥锁,Mutual Exclusion)是标准库 std::sync 模块提供的核心同步原语。它的主要作用是在多线程环境下保护共享数据,确保同一时刻只有一个线程可以访问或修改该数据,从而避免数据竞争(Data Race)。
8.1 Mutex::new(data):创建Mutex
- 功能 :创建一个新的
Mutex,包裹初始数据。 - 用途:初始化共享状态。
8.2 .lock():阻塞获取锁
- 功能 :尝试获取互斥锁。如果锁当前被其他线程持有,当前线程会阻塞(进入睡眠状态),直到锁被释放。
- 用途:这是最常用的方法,适用于必须获取锁才能继续执行的场景。
- 注意事项 :
- 返回的
MutexGuard实现了Deref和DerefMut,可以直接操作内部数据。 - 当
MutexGuard离开作用域时,析构函数会自动释放锁。 - 如果前一个持有锁的线程发生了恐慌(Panic),锁会被标记为"中毒"(Poisoned),此时
.lock()会返回Err。通常使用.unwrap()处理,但在生产环境中建议妥善处理PoisonError。
- 返回的
rust
use std::sync::Mutex;
fn main() {
let v = Mutex::new(1);
// 1. 获取锁并修改值
let mut v1 = v.lock().unwrap();
*v1 += 2;
println!("{:?}", v1);
drop(v1); // 释放锁
// 2. 再次获取锁
match v.lock() {
Ok(mut v2) => {
*v2 += 3;
println!("{:?}", v2)
}
Err(e) => {
println!("锁中毒: {:?}", e);
}
}
}
8.3 .try_lock():非阻塞尝试获取锁
- 功能 :尝试立即获取锁。如果锁可用,则获取并返回
Ok(Guard);如果锁已被占用,则立即返回错误,不会阻塞当前线程。 - 用途:适用于需要避免死锁、实现自定义重试逻辑、或在无法获取锁时需要执行其他备用逻辑的场景。
rust
match v.try_lock() {
Ok(mut v3) => {
*v3 += 4;
println!("{:?}", v3)
}
Err(e) => match e {
std::sync::TryLockError::Poisoned(_) => {
println!("锁中毒: {:?}", e);
}
std::sync::TryLockError::WouldBlock => {
println!("锁已被占用: {:?}", e);
}
},
}
8.4 .into_inner():消费 Mutex 提取数据
-
功能 :消费(Consume)
Mutex本身,返回内部包裹的数据T。 -
用途:
- 当不再需要多线程同步,希望将数据转移出来时使用。
- 常用于程序结束前获取最终结果,或在单线程上下文中解开包装。
-
注意 :调用后
Mutex实例将被销毁,无法再使用。
rust
match v.into_inner() {
Ok(mut v4) => {
v4 += 5;
println!("{:?}", v4)
}
Err(e) => {
println!("锁中毒: {:?}", e);
}
}
// v 已销毁,无法再使用
8.5 .get_mut():无锁获取可变引用
-
功能 :获取内部数据的可变引用
&mut T。 -
用途:
- 无需加锁 。因为该方法要求借用
&mut self,编译器静态保证了此时没有其他线程或守卫持有该锁。 - 适用于初始化阶段、单线程环境,或在确定没有并发访问时的性能优化。
- 无需加锁 。因为该方法要求借用
-
注意 :如果在多线程环境下错误地使用(例如通过
unsafe绕过检查),会导致数据竞争。
rust
let mut v = Mutex::new(1);
match v.get_mut() {
Ok(vv) => {
*vv += 1;
println!("{:?}", vv);
}
Err(e) => {
println!("锁中毒: {:?}", e);
}
}
8.6 PoisonError::into_inner():恢复中毒数据
所有返回 LockResult 的方法(lock, try_lock, into_inner, get_mut)都可能返回 PoisonError。
- 恢复数据 :可以通过
PoisonError::into_inner()从错误中提取出MutexGuard或数据,继续访问。这表示开发者承认数据可能不一致,但决定继续使用。
rust
match v.lock() {
Ok(mut v2) => {
*v2 += 3;
println!("{:?}", v2)
}
Err(e) => {
println!("锁中毒: {:?}", e);
let mut v0 = e.into_inner(); // 从错误中提取出 MutexGuard 或数据
*v0 = 0; // 重新初始化数据为0
}
}