MST

星途 面试题库

面试题:Rust互斥多线程中的死锁问题

在使用`Mutex`进行多线程并发控制时,死锁是一个常见问题。请描述在Rust中可能导致死锁的场景,并给出至少两种避免死锁的策略,同时结合代码示例说明如何使用`std::sync::Mutex`的`try_lock`方法来检测和避免死锁。
19.3万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

可能导致死锁的场景

  1. 循环依赖:线程A持有锁1,试图获取锁2;线程B持有锁2,试图获取锁1。这就形成了循环依赖,导致死锁。例如:
use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let lock1 = Arc::new(Mutex::new(0));
    let lock2 = Arc::new(Mutex::new(0));

    let lock1_clone = lock1.clone();
    let lock2_clone = lock2.clone();

    let thread1 = thread::spawn(move || {
        let _guard1 = lock1_clone.lock().unwrap();
        thread::sleep(std::time::Duration::from_secs(1));
        let _guard2 = lock2_clone.lock().unwrap();
    });

    let thread2 = thread::spawn(move || {
        let _guard2 = lock2.lock().unwrap();
        thread::sleep(std::time::Duration::from_secs(1));
        let _guard1 = lock1.lock().unwrap();
    });

    thread1.join().unwrap();
    thread2.join().unwrap();
}
  1. 嵌套锁获取顺序不一致:不同线程以不同顺序获取多个锁,可能导致死锁。

避免死锁的策略

  1. 固定锁获取顺序:所有线程都按照相同的顺序获取锁。例如,总是先获取lock1,再获取lock2
use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let lock1 = Arc::new(Mutex::new(0));
    let lock2 = Arc::new(Mutex::new(0));

    let lock1_clone = lock1.clone();
    let lock2_clone = lock2.clone();

    let thread1 = thread::spawn(move || {
        let _guard1 = lock1_clone.lock().unwrap();
        let _guard2 = lock2_clone.lock().unwrap();
    });

    let thread2 = thread::spawn(move || {
        let _guard1 = lock1.lock().unwrap();
        let _guard2 = lock2.lock().unwrap();
    });

    thread1.join().unwrap();
    thread2.join().unwrap();
}
  1. 使用超时机制:在获取锁时设置超时时间,如果在超时时间内没有获取到锁,则放弃操作,避免无限等待。

使用try_lock方法检测和避免死锁

try_lock方法尝试获取锁,如果锁可用则返回一个Result包含锁的Guard,如果锁不可用则立即返回Err

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

fn main() {
    let lock1 = Arc::new(Mutex::new(0));
    let lock2 = Arc::new(Mutex::new(0));

    let lock1_clone = lock1.clone();
    let lock2_clone = lock2.clone();

    let thread1 = thread::spawn(move || {
        if let Ok(_guard1) = lock1_clone.try_lock() {
            if let Ok(_guard2) = lock2_clone.try_lock() {
                // 成功获取两个锁,可以进行操作
            } else {
                // 未能获取锁2,释放锁1
                drop(_guard1);
            }
        }
    });

    let thread2 = thread::spawn(move || {
        if let Ok(_guard2) = lock2.lock() {
            if let Ok(_guard1) = lock1.try_lock() {
                // 成功获取两个锁,可以进行操作
            } else {
                // 未能获取锁1,释放锁2
                drop(_guard2);
            }
        }
    });

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

通过try_lock方法,线程可以尝试获取锁并根据结果决定是否继续等待或放弃,从而避免死锁。