面试题答案
一键面试死锁产生的原因
- 资源竞争:多个线程同时尝试获取多个资源,并且获取资源的顺序不一致。例如,线程A获取资源1,然后尝试获取资源2;而线程B获取资源2,然后尝试获取资源1。如果两个线程都持有自己获取的资源不释放,就会导致死锁。
- 循环等待:线程之间形成一个环形的等待链,每个线程都在等待下一个线程释放资源,最终形成死锁。
Rust中预防死锁的方法
- 按照固定顺序获取锁:在需要获取多个锁时,所有线程都按照相同的顺序获取锁。例如,如果需要获取
Mutex1
和Mutex2
,所有线程都先获取Mutex1
,再获取Mutex2
。这样可以避免循环等待的情况。 - 使用
lock_order
宏:Rust标准库提供了lock_order
宏来帮助检测锁的获取顺序。如果不按照指定顺序获取锁,编译器会报错。该宏在std::sync::lock_order
模块中。 - 限时锁获取:使用
try_lock
方法尝试获取锁,如果在一定时间内获取不到锁,则放弃获取,避免无限期等待。例如Mutex::try_lock
和RwLock::try_lock
。
可能导致死锁的场景及解决方案
场景
use std::sync::{Mutex, Arc};
use std::thread;
fn main() {
let mutex1 = Arc::new(Mutex::new(0));
let mutex2 = Arc::new(Mutex::new(0));
let mutex1_clone = mutex1.clone();
let mutex2_clone = mutex2.clone();
let thread1 = thread::spawn(move || {
let _guard1 = mutex1_clone.lock().unwrap();
thread::sleep(std::time::Duration::from_millis(100));
let _guard2 = mutex2_clone.lock().unwrap();
});
let thread2 = thread::spawn(move || {
let _guard2 = mutex2.lock().unwrap();
thread::sleep(std::time::Duration::from_millis(100));
let _guard1 = mutex1.lock().unwrap();
});
thread1.join().unwrap();
thread2.join().unwrap();
}
在这个例子中,thread1
先获取 mutex1
然后尝试获取 mutex2
,thread2
先获取 mutex2
然后尝试获取 mutex1
。如果两个线程都获取了第一个锁,就会互相等待对方释放锁,从而导致死锁。
解决方案
- 固定顺序获取锁:修改代码,让两个线程都按照相同顺序获取锁。
use std::sync::{Mutex, Arc};
use std::thread;
fn main() {
let mutex1 = Arc::new(Mutex::new(0));
let mutex2 = Arc::new(Mutex::new(0));
let mutex1_clone = mutex1.clone();
let mutex2_clone = mutex2.clone();
let thread1 = thread::spawn(move || {
let _guard1 = mutex1_clone.lock().unwrap();
thread::sleep(std::time::Duration::from_millis(100));
let _guard2 = mutex2_clone.lock().unwrap();
});
let thread2 = thread::spawn(move || {
let _guard1 = mutex1.lock().unwrap();
thread::sleep(std::time::Duration::from_millis(100));
let _guard2 = mutex2.lock().unwrap();
});
thread1.join().unwrap();
thread2.join().unwrap();
}
- 使用
try_lock
:修改代码,使用try_lock
方法避免无限期等待。
use std::sync::{Mutex, Arc};
use std::thread;
fn main() {
let mutex1 = Arc::new(Mutex::new(0));
let mutex2 = Arc::new(Mutex::new(0));
let mutex1_clone = mutex1.clone();
let mutex2_clone = mutex2.clone();
let thread1 = thread::spawn(move || {
if let Ok(_guard1) = mutex1_clone.try_lock() {
if let Ok(_guard2) = mutex2_clone.try_lock() {
// 执行相关操作
}
}
});
let thread2 = thread::spawn(move || {
if let Ok(_guard1) = mutex1.try_lock() {
if let Ok(_guard2) = mutex2.try_lock() {
// 执行相关操作
}
}
});
thread1.join().unwrap();
thread2.join().unwrap();
}