MST

星途 面试题库

面试题:Rust线程间状态共享时的死锁问题及预防

在使用 `Mutex` 或者 `RwLock` 进行线程间状态共享时,死锁是一个潜在的问题。请阐述死锁产生的原因,并且说明在Rust中如何预防死锁的发生,同时举例说明一个可能导致死锁的场景及对应的解决方案。
18.2万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

死锁产生的原因

  1. 资源竞争:多个线程同时尝试获取多个资源,并且获取资源的顺序不一致。例如,线程A获取资源1,然后尝试获取资源2;而线程B获取资源2,然后尝试获取资源1。如果两个线程都持有自己获取的资源不释放,就会导致死锁。
  2. 循环等待:线程之间形成一个环形的等待链,每个线程都在等待下一个线程释放资源,最终形成死锁。

Rust中预防死锁的方法

  1. 按照固定顺序获取锁:在需要获取多个锁时,所有线程都按照相同的顺序获取锁。例如,如果需要获取 Mutex1Mutex2,所有线程都先获取 Mutex1,再获取 Mutex2。这样可以避免循环等待的情况。
  2. 使用 lock_order:Rust标准库提供了 lock_order 宏来帮助检测锁的获取顺序。如果不按照指定顺序获取锁,编译器会报错。该宏在 std::sync::lock_order 模块中。
  3. 限时锁获取:使用 try_lock 方法尝试获取锁,如果在一定时间内获取不到锁,则放弃获取,避免无限期等待。例如 Mutex::try_lockRwLock::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 然后尝试获取 mutex2thread2 先获取 mutex2 然后尝试获取 mutex1。如果两个线程都获取了第一个锁,就会互相等待对方释放锁,从而导致死锁。

解决方案

  1. 固定顺序获取锁:修改代码,让两个线程都按照相同顺序获取锁。
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();
}
  1. 使用 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();
}