Skip to content

Ch 30: std::sync::Mutex - 互斥锁

std::sync::Mutex(互斥锁)提供了一种方式来保护共享数据,确保同一时间只有一个线程可以访问。Rust的Mutex是 PoisonError感知的,即如果持有锁的线程panic,锁会自动”中毒”。

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());
}
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>>
}
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();
}

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;
}

当持有锁的线程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);
}
}
}

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());
}
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!("避免死锁");
}
  1. 锁粒度:锁的粒度要适当,太粗降低并发,太细增加开销
  2. 不要在持有锁时执行耗时操作:这会阻塞其他线程
  3. 避免死锁:始终按相同顺序获取多个锁
  4. PoisonError:可以使用into_inner()恢复数据
  5. Guard生命周期:Guard被drop时自动释放锁

std::sync::Mutex核心API:

  • Mutex::new() - 创建互斥锁
  • lock() - 获取锁(阻塞)
  • try_lock() - 尝试获取锁(非阻塞)
  • PoisonError - 锁中毒错误处理
  • MutexGuard - 自动释放的锁守卫

Mutex + Arc是Rust中最常用的线程安全共享状态模式:

let data = Arc::new(Mutex::new(初始值));
// 跨线程clone Arc