MST
星途 面试题库

面试题:Rust释放和获取顺序在复杂多锁场景中的应用与问题

在一个有多个`Mutex`的Rust多线程程序中,不同线程可能会以不同顺序获取这些锁。请阐述如何避免死锁,并结合释放和获取顺序原理,分析以下代码存在的潜在问题并给出改进方案: ```rust use std::sync::{Arc, Mutex}; fn main() { let mutex1 = Arc::new(Mutex::new(0)); let mutex2 = Arc::new(Mutex::new(1)); let mutex1_clone = mutex1.clone(); let mutex2_clone = mutex2.clone(); std::thread::spawn(move || { let _lock1 = mutex1_clone.lock().unwrap(); std::thread::sleep(std::time::Duration::from_secs(1)); let _lock2 = mutex2_clone.lock().unwrap(); }); let _lock2 = mutex2.lock().unwrap(); std::thread::sleep(std::time::Duration::from_secs(1)); let _lock1 = mutex1.lock().unwrap(); } ```
37.2万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

避免死锁的方法

在Rust多线程程序中,当涉及多个Mutex且不同线程可能以不同顺序获取锁时,为避免死锁,可以采用以下方法:

  1. 固定锁获取顺序:所有线程都按照相同的顺序获取锁。这样可以避免形成循环等待的情况,因为如果所有线程都以相同顺序获取锁,就不会出现一个线程持有锁A等待锁B,而另一个线程持有锁B等待锁A的情况。
  2. 使用std::sync::TryLockError进行尝试获取锁:通过使用try_lock方法,线程可以尝试获取锁,如果获取失败,可以释放已获取的锁并重新尝试,或者采取其他措施,而不是一直阻塞等待,从而避免死锁。

分析代码潜在问题

上述代码存在死锁风险。主线程和新创建的线程获取锁的顺序不同。主线程先获取mutex2,然后获取mutex1;而新线程先获取mutex1,然后获取mutex2。如果主线程获取mutex2后,新线程获取mutex1,两个线程将互相等待对方释放锁,从而导致死锁。

改进方案

  1. 固定锁获取顺序:让两个线程都以相同顺序获取锁,例如都先获取mutex1,再获取mutex2
use std::sync::{Arc, Mutex};

fn main() {
    let mutex1 = Arc::new(Mutex::new(0));
    let mutex2 = Arc::new(Mutex::new(1));

    let mutex1_clone = mutex1.clone();
    let mutex2_clone = mutex2.clone();

    std::thread::spawn(move || {
        let _lock1 = mutex1_clone.lock().unwrap();
        std::thread::sleep(std::time::Duration::from_secs(1));
        let _lock2 = mutex2_clone.lock().unwrap();
    });

    let _lock1 = mutex1.lock().unwrap();
    std::thread::sleep(std::time::Duration::from_secs(1));
    let _lock2 = mutex2.lock().unwrap();
}
  1. 使用try_lock尝试获取锁:主线程和新线程使用try_lock尝试获取锁,如果获取失败,释放已获取的锁并重新尝试。
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;

fn main() {
    let mutex1 = Arc::new(Mutex::new(0));
    let mutex2 = Arc::new(Mutex::new(1));

    let mutex1_clone = mutex1.clone();
    let mutex2_clone = mutex2.clone();

    thread::spawn(move || {
        loop {
            match (mutex1_clone.try_lock(), mutex2_clone.try_lock()) {
                (Ok(lock1), Ok(lock2)) => {
                    drop(lock1);
                    drop(lock2);
                    break;
                }
                _ => {
                    thread::sleep(Duration::from_millis(100));
                }
            }
        }
    });

    loop {
        match (mutex1.try_lock(), mutex2.try_lock()) {
            (Ok(lock1), Ok(lock2)) => {
                drop(lock1);
                drop(lock2);
                break;
            }
            _ => {
                thread::sleep(Duration::from_millis(100));
            }
        }
    }
}