MST

星途 面试题库

面试题:Rust多线程编程中Mutex与Arc的死锁分析及解决

编写一段Rust代码,其中使用`Mutex`和`Arc`创建多个线程,有意引入死锁情况(例如通过线程间循环依赖锁)。然后分析死锁产生的原因,并给出至少一种避免死锁的方法及修改后的代码。
36.1万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

引入死锁的代码

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let resource_a = Arc::new(Mutex::new(0));
    let resource_b = Arc::new(Mutex::new(0));

    let resource_a_clone = resource_a.clone();
    let resource_b_clone = resource_b.clone();

    let thread1 = thread::spawn(move || {
        let _lock_a = resource_a_clone.lock().unwrap();
        thread::sleep(std::time::Duration::from_millis(100));
        let _lock_b = resource_b_clone.lock().unwrap();
        println!("Thread 1 acquired both locks");
    });

    let thread2 = thread::spawn(move || {
        let _lock_b = resource_b.lock().unwrap();
        thread::sleep(std::time::Duration::from_millis(100));
        let _lock_a = resource_a.lock().unwrap();
        println!("Thread 2 acquired both locks");
    });

    thread1.join().unwrap();
    thread2.join().unwrap();
}

死锁产生原因分析

死锁产生是因为thread1首先获取resource_a的锁,然后尝试获取resource_b的锁;而thread2首先获取resource_b的锁,然后尝试获取resource_a的锁。当thread1获取了resource_a的锁,thread2获取了resource_b的锁时,它们相互等待对方释放锁,从而陷入死锁。

避免死锁的方法及修改后代码

方法一:按照固定顺序获取锁

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let resource_a = Arc::new(Mutex::new(0));
    let resource_b = Arc::new(Mutex::new(0));

    let resource_a_clone = resource_a.clone();
    let resource_b_clone = resource_b.clone();

    let thread1 = thread::spawn(move || {
        let _lock_a = resource_a_clone.lock().unwrap();
        let _lock_b = resource_b_clone.lock().unwrap();
        println!("Thread 1 acquired both locks");
    });

    let thread2 = thread::spawn(move || {
        let _lock_a = resource_a.lock().unwrap();
        let _lock_b = resource_b.lock().unwrap();
        println!("Thread 2 acquired both locks");
    });

    thread1.join().unwrap();
    thread2.join().unwrap();
}

在这个修改后的代码中,thread1thread2都按照先获取resource_a的锁,再获取resource_b的锁的顺序进行操作,避免了循环依赖,从而避免了死锁。

方法二:使用try_lock

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let resource_a = Arc::new(Mutex::new(0));
    let resource_b = Arc::new(Mutex::new(0));

    let resource_a_clone = resource_a.clone();
    let resource_b_clone = resource_b.clone();

    let thread1 = thread::spawn(move || {
        let lock_a = resource_a_clone.try_lock();
        if let Ok(mut _lock_a) = lock_a {
            let lock_b = resource_b_clone.try_lock();
            if let Ok(mut _lock_b) = lock_b {
                println!("Thread 1 acquired both locks");
            } else {
                println!("Thread 1 could not acquire lock B, releasing lock A");
            }
        }
    });

    let thread2 = thread::spawn(move || {
        let lock_b = resource_b.try_lock();
        if let Ok(mut _lock_b) = lock_b {
            let lock_a = resource_a.try_lock();
            if let Ok(mut _lock_a) = lock_a {
                println!("Thread 2 acquired both locks");
            } else {
                println!("Thread 2 could not acquire lock A, releasing lock B");
            }
        }
    });

    thread1.join().unwrap();
    thread2.join().unwrap();
}

在这个代码中,使用try_lock尝试获取锁,如果获取失败,就释放已经获取的锁,避免了死锁。