面试题答案
一键面试1. 什么是虚假唤醒
虚假唤醒是指在多线程编程中,条件变量(Condvar
)在没有相应 notify
操作的情况下,线程被唤醒的现象。这不是由于错误,而是底层操作系统或线程库实现的一种特性。通常在使用 Condvar
等待某个条件满足时,期望只有在其他线程调用 notify_one
或 notify_all
时才唤醒等待的线程,但虚假唤醒打破了这个预期。
2. 虚假唤醒在并发场景下带来的问题
- 数据不一致:如果线程被虚假唤醒并继续执行,可能会在条件实际上未满足的情况下访问和修改共享数据,导致数据处于不一致的状态。
- 逻辑错误:依赖条件变量的业务逻辑可能会因为虚假唤醒而执行错误的分支,导致程序出现错误的行为,例如提前终止或执行不必要的计算。
3. 在Rust代码中检测和处理虚假唤醒
在Rust中,可以通过在循环中检查条件来处理虚假唤醒。当 Condvar
的 wait
方法返回时,再次检查条件是否真的满足。
代码示例
use std::sync::{Arc, Mutex, Condvar};
use std::thread;
fn main() {
let data = Arc::new((Mutex::new(false), Condvar::new()));
let data_clone = data.clone();
// 等待线程
let waiter = thread::spawn(move || {
let (lock, cvar) = &*data_clone;
let mut data = lock.lock().unwrap();
while!*data {
data = cvar.wait(data).unwrap();
}
println!("等待线程被唤醒,条件已满足");
});
// 通知线程
let notifier = thread::spawn(move || {
let (lock, _) = &*data;
let mut data = lock.lock().unwrap();
*data = true;
println!("通知线程设置条件为true");
drop(data);
(lock, _).1.notify_one();
});
waiter.join().unwrap();
notifier.join().unwrap();
}
在上述代码中:
- 定义共享数据:使用
Arc
来跨线程共享(Mutex<bool>, Condvar)
数据结构。 - 等待线程:在
while!*data
循环中调用cvar.wait(data)
,即使被虚假唤醒,也会再次检查*data
是否为true
,只有当条件满足时才会继续执行。 - 通知线程:设置
*data
为true
并调用notify_one
通知等待线程。这样可以确保等待线程在条件真正满足时才执行后续逻辑,避免了虚假唤醒带来的问题。