MST
星途 面试题库

面试题:Rust多线程错误处理中的条件变量与死锁预防

假设你在Rust多线程程序中使用条件变量(Condvar)来协调线程间的同步。请描述可能导致死锁的场景,并阐述如何通过合理的错误处理策略和Rust语言特性来预防这种死锁情况,给出相应的代码示例并解释。
43.6万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

可能导致死锁的场景

  1. 忘记通知:一个线程在等待条件变量,而另一个应该通知条件变量的线程永远不执行通知操作,导致等待线程永远阻塞。
  2. 锁顺序不一致:不同线程以不同顺序获取和释放锁,例如线程A获取锁lock_a,然后尝试获取锁lock_b,而线程B先获取锁lock_b,再尝试获取锁lock_a,如果两个线程同时进行这些操作,就会发生死锁。
  3. 持有锁进行通知:在持有条件变量关联的互斥锁时进行通知,可能会导致等待线程获取锁失败,因为通知线程持有锁,等待线程无法获取锁从而继续等待,而通知线程又在等待等待线程完成某些操作后释放锁,形成死锁。

预防死锁的策略

  1. 确保通知:在适当的时机调用notify_onenotify_all方法通知等待的线程。
  2. 固定锁顺序:在所有线程中以相同的顺序获取和释放锁。
  3. 释放锁后通知:在通知条件变量之前释放与之关联的互斥锁。

代码示例

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

fn main() {
    let pair = Arc::new((Mutex::new(false), Condvar::new()));
    let pair2 = pair.clone();

    // 等待线程
    let handle = thread::spawn(move || {
        let (lock, cvar) = &*pair;
        let mut data = lock.lock().unwrap();
        while!*data {
            data = cvar.wait(data).unwrap();
        }
        println!("等待线程收到通知");
    });

    // 通知线程
    thread::sleep(std::time::Duration::from_secs(1));
    let (lock, cvar) = &*pair2;
    {
        let mut data = lock.lock().unwrap();
        *data = true;
    } // 离开作用域,释放锁
    cvar.notify_one();

    handle.join().unwrap();
}

代码解释

  1. 创建条件变量和互斥锁:通过ArcMutexCondvar创建共享数据和同步工具。
  2. 等待线程:使用while循环和cvar.wait等待条件变量通知,确保即使虚假唤醒也能正确等待。
  3. 通知线程:先获取锁,修改共享数据,然后离开作用域释放锁,最后调用notify_one通知等待线程。这样避免了在持有锁时通知导致的死锁问题。同时通过固定锁获取和释放顺序,以及确保通知操作,预防了死锁的发生。