MST

星途 面试题库

面试题:Rust 条件变量线程唤醒中的异常处理

当在条件变量的`wait`方法中可能出现`Err`时,在Rust多线程编程里应如何合理处理这种情况,以保证线程安全和程序的健壮性?结合具体代码说明不同处理方式的优缺点。
50.3万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

在Rust多线程编程中,std::sync::Condvarwait方法返回一个Result<(), PoisonError<()>>PoisonError通常表示互斥锁被 poisoned,这意味着在其他线程中发生了恐慌(panic),导致互斥锁处于一种未定义状态。以下是不同处理方式及其优缺点。

方式一:忽略错误

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

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

    thread::spawn(move || {
        let (lock, cvar) = &*pair2;
        let mut data = lock.lock().unwrap();
        *data = true;
        cvar.notify_one();
    });

    let (lock, cvar) = &*pair;
    let mut data = lock.lock().unwrap();
    while!*data {
        data = cvar.wait(data).unwrap();
    }
}

优点

  • 代码简单直观,在大多数情况下,这种简单处理方式能满足需求,尤其是当你确定其他线程不会 panic 时。

缺点

  • 忽略了PoisonError可能带来的问题。如果其他线程 panic 导致互斥锁 poisoned,程序可能会继续运行在未定义状态,可能引发难以调试的错误。

方式二:传播错误

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

fn main() -> Result<(), PoisonError<()>> {
    let pair = Arc::new((Mutex::new(false), Condvar::new()));
    let pair2 = pair.clone();

    thread::spawn(move || {
        let (lock, cvar) = &*pair2;
        let mut data = lock.lock().unwrap();
        *data = true;
        cvar.notify_one();
    });

    let (lock, cvar) = &*pair;
    let mut data = lock.lock()?;
    while!*data {
        data = cvar.wait(data)?;
    }
    Ok(())
}

优点

  • 严谨地处理了PoisonError,使得调用栈上的上层函数能够知道并处理这种错误情况,增强了程序的健壮性。

缺点

  • 调用main函数的地方需要处理PoisonError,增加了调用者的负担。如果调用链很长,处理错误会变得繁琐。

方式三:清理并继续

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

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

    thread::spawn(move || {
        let (lock, cvar) = &*pair2;
        let mut data = lock.lock().unwrap();
        *data = true;
        cvar.notify_one();
    });

    let (lock, cvar) = &*pair;
    let mut data = match lock.lock() {
        Ok(guard) => guard,
        Err(e) => e.into_inner(),
    };
    while!*data {
        data = match cvar.wait(data) {
            Ok(guard) => guard,
            Err(e) => e.into_inner(),
        };
    }
}

优点

  • 当遇到PoisonError时,尝试清理互斥锁并继续执行。这样可以在一定程度上保证程序不会因为互斥锁被 poisoned 而崩溃,维持线程安全。

缺点

  • 这种方式假设程序在互斥锁 poisoned 后仍能正常工作,但实际上在某些复杂逻辑中,这可能并不成立。而且它掩盖了其他线程 panic 的事实,不利于调试。