面试题答案
一键面试常见导致死锁的场景
- 循环依赖:多个线程互相等待对方持有的资源,形成一个环形依赖。例如线程A持有资源R1并等待资源R2,而线程B持有资源R2并等待资源R1。
- 锁顺序不一致:不同线程以不同顺序获取锁。假设线程1先获取锁A再获取锁B,而线程2先获取锁B再获取锁A,如果线程1获取锁A后,线程2获取锁B,此时双方再尝试获取对方已持有的锁就会导致死锁。
避免死锁的方法
- 使用资源分配图算法(如银行家算法):该算法可以在资源分配前检查是否会导致死锁,但实现较为复杂,在Rust中不常用。
- 按固定顺序获取锁:确保所有线程都以相同的顺序获取锁。在同步通道场景下,可以理解为按固定顺序操作通道。
代码示例
use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;
fn main() {
let (tx1, rx1): (Sender<i32>, Receiver<i32>) = channel();
let (tx2, rx2): (Sender<i32>, Receiver<i32>) = channel();
let handle1 = thread::spawn(move || {
// 按固定顺序发送数据
tx1.send(1).unwrap();
let data = rx2.recv().unwrap();
println!("Thread 1 received: {}", data);
});
let handle2 = thread::spawn(move || {
// 按固定顺序发送数据
tx2.send(2).unwrap();
let data = rx1.recv().unwrap();
println!("Thread 2 received: {}", data);
});
handle1.join().unwrap();
handle2.join().unwrap();
}
在上述代码中,两个线程按相同顺序操作通道,避免了死锁。如果不按此固定顺序,例如线程1先从rx2
接收再向tx1
发送,线程2先从rx1
接收再向tx2
发送,就可能会导致死锁。