跳到主要内容

死锁排查

问题

Rust 中会发生死锁吗?如何排查和预防?

答案

Rust 的类型系统可以防止数据竞争,但不能防止死锁。死锁是逻辑问题,不是内存安全问题。

常见死锁场景

1. 锁顺序不一致

use std::sync::Mutex;

let lock_a = Mutex::new(1);
let lock_b = Mutex::new(2);

// 线程 1:先锁 A,再锁 B
let _a = lock_a.lock().unwrap();
let _b = lock_b.lock().unwrap(); // 等待 B

// 线程 2:先锁 B,再锁 A
let _b = lock_b.lock().unwrap();
let _a = lock_a.lock().unwrap(); // 等待 A → 死锁!

// ✅ 修复:统一锁顺序(总是先锁 A)

2. 异步代码中的死锁

use std::sync::Mutex; // 标准库的 Mutex

async fn deadlock_risk(data: &Mutex<Vec<i32>>) {
let guard = data.lock().unwrap();
// 如果在持有 guard 时 .await,当前 task 被挂起
// 但 Mutex guard 未释放
// 其他 task 无法获取锁
some_async_operation().await; // ← 持有锁跨 await
drop(guard);
}

// ✅ 修复:在 .await 之前释放锁
async fn safe(data: &Mutex<Vec<i32>>) {
let value = {
let guard = data.lock().unwrap();
guard.clone() // 拷贝后立即释放锁
};
some_async_operation().await;
}

3. 同一线程重复获取非可重入锁

let lock = Mutex::new(0);
let _guard1 = lock.lock().unwrap();
let _guard2 = lock.lock().unwrap(); // 死锁!同一线程重复锁

// ✅ 修复:重构代码,避免重复锁
// 或使用 parking_lot::ReentrantMutex

死锁排查方法

方法工具
线程转储gdb / lldb 查看线程栈
锁依赖图手动分析或 no-deadlocks crate
SanitizerRUSTFLAGS="-Zsanitizer=thread"
超时检测parking_lot::Mutextry_lock_for

常见面试问题

Q1: 如何预防 Rust 中的死锁?

答案

  1. 统一锁顺序:全局定义锁的获取顺序
  2. 缩小锁范围:尽快释放锁,不跨 .await
  3. 使用 tokio::sync::Mutex:在异步代码中,它可以跨 await
  4. 超时try_lock_for 设置获取锁的超时时间
  5. 减少锁使用:用 Channel、Arc<AtomicXxx> 等无锁方案

相关链接