死锁场景一:循环依赖
- 场景示例:
假设有两个线程
thread1
和 thread2
,thread1
持有锁 lock1
并尝试获取锁 lock2
,而 thread2
持有锁 lock2
并尝试获取锁 lock1
。这就形成了循环依赖,导致死锁。
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let lock1 = Arc::new(Mutex::new(()));
let lock2 = Arc::new(Mutex::new(()));
let lock1_clone = lock1.clone();
let lock2_clone = lock2.clone();
let thread1 = thread::spawn(move || {
let _guard1 = lock1_clone.lock().unwrap();
let _guard2 = lock2_clone.lock().unwrap();
});
let thread2 = thread::spawn(move || {
let _guard2 = lock2.lock().unwrap();
let _guard1 = lock1.lock().unwrap();
});
thread1.join().unwrap();
thread2.join().unwrap();
}
- 防范措施:
- 固定锁获取顺序:所有线程按照相同的顺序获取锁。例如,始终先获取
lock1
,再获取 lock2
。这样就不会出现循环依赖。
- 使用
std::sync::TryLockError
:使用 try_lock
方法尝试获取锁,如果获取失败可以进行回退或重试,而不是一直等待。例如:
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let lock1 = Arc::new(Mutex::new(()));
let lock2 = Arc::new(Mutex::new(()));
let lock1_clone = lock1.clone();
let lock2_clone = lock2.clone();
let thread1 = thread::spawn(move || {
match (lock1_clone.try_lock(), lock2_clone.try_lock()) {
(Ok(_guard1), Ok(_guard2)) => (),
_ => return,
}
});
let thread2 = thread::spawn(move || {
match (lock1.try_lock(), lock2.try_lock()) {
(Ok(_guard1), Ok(_guard2)) => (),
_ => return,
}
});
thread1.join().unwrap();
thread2.join().unwrap();
}
死锁场景二:锁层次混乱
- 场景示例:
假设有三个线程
thread1
、thread2
和 thread3
,thread1
获取锁 lock1
后调用一个函数 func1
,func1
又尝试获取锁 lock2
。thread2
获取锁 lock2
后调用 func2
,func2
尝试获取锁 lock3
。thread3
获取锁 lock3
后尝试获取锁 lock1
。这种复杂的锁获取层次关系可能导致死锁。
use std::sync::{Arc, Mutex};
use std::thread;
fn func1(lock2: Arc<Mutex<()>>) {
let _guard2 = lock2.lock().unwrap();
}
fn func2(lock3: Arc<Mutex<()>>) {
let _guard3 = lock3.lock().unwrap();
}
fn main() {
let lock1 = Arc::new(Mutex::new(()));
let lock2 = Arc::new(Mutex::new(()));
let lock3 = Arc::new(Mutex::new(()));
let lock1_clone = lock1.clone();
let lock2_clone = lock2.clone();
let lock3_clone = lock3.clone();
let thread1 = thread::spawn(move || {
let _guard1 = lock1_clone.lock().unwrap();
func1(lock2.clone());
});
let thread2 = thread::spawn(move || {
let _guard2 = lock2_clone.lock().unwrap();
func2(lock3.clone());
});
let thread3 = thread::spawn(move || {
let _guard3 = lock3_clone.lock().unwrap();
let _guard1 = lock1.lock().unwrap();
});
thread1.join().unwrap();
thread2.join().unwrap();
thread3.join().unwrap();
}
- 防范措施:
- 锁层次规划:明确锁的层次结构,确保高级别的锁不会依赖低级别的锁。例如,可以通过设计一个锁获取顺序表,所有函数和线程按照这个顺序获取锁。
- 使用
MutexGuard
作用域:确保 MutexGuard
在函数返回前释放锁,避免在函数调用过程中锁的持有混乱。例如,将获取锁的代码块限制在最小作用域内。
use std::sync::{Arc, Mutex};
use std::thread;
fn func1(lock2: Arc<Mutex<()>>) {
{
let _guard2 = lock2.lock().unwrap();
// 仅在这个块内持有锁
}
}
fn func2(lock3: Arc<Mutex<()>>) {
{
let _guard3 = lock3.lock().unwrap();
// 仅在这个块内持有锁
}
}
fn main() {
let lock1 = Arc::new(Mutex::new(()));
let lock2 = Arc::new(Mutex::new(()));
let lock3 = Arc::new(Mutex::new(()));
let lock1_clone = lock1.clone();
let lock2_clone = lock2.clone();
let lock3_clone = lock3.clone();
let thread1 = thread::spawn(move || {
{
let _guard1 = lock1_clone.lock().unwrap();
func1(lock2.clone());
}
});
let thread2 = thread::spawn(move || {
{
let _guard2 = lock2_clone.lock().unwrap();
func2(lock3.clone());
}
});
let thread3 = thread::spawn(move || {
{
let _guard3 = lock3_clone.lock().unwrap();
// 这里不能再获取lock1,按照锁层次规划
}
});
thread1.join().unwrap();
thread2.join().unwrap();
thread3.join().unwrap();
}