面试题答案
一键面试在Rust多线程编程中,std::sync::Condvar
的wait
方法返回一个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 的事实,不利于调试。