面试题答案
一键面试可能出现性能问题的点
- 频繁唤醒与上下文切换:多个线程等待条件变量,每次条件变量通知时,所有等待线程都会被唤醒,即使只有部分线程满足条件。这导致大量不必要的线程上下文切换,消耗CPU资源。
- 锁竞争:条件变量通常与互斥锁配合使用。在高并发环境下,对互斥锁的频繁获取和释放可能导致锁竞争,降低系统并发性能。
- 虚假唤醒:在某些操作系统中,线程可能会被虚假唤醒,即没有收到条件变量的通知就被唤醒,这也会浪费CPU资源。
优化策略
- 减少不必要唤醒:使用更细粒度的通知机制,例如为不同类型的条件创建不同的条件变量,使得只有满足特定条件的线程被唤醒。
- 降低锁竞争:采用读写锁(
RwLock
)或其他更适合高并发读的锁机制,减少对互斥锁的依赖。对于读多写少的场景,读写锁可以显著提高并发性能。 - 处理虚假唤醒:在条件变量唤醒后,再次检查条件是否真正满足,避免因虚假唤醒而执行不必要的操作。
Rust代码示例
use std::sync::{Arc, Condvar, Mutex};
use std::thread;
fn main() {
let data = Arc::new((Mutex::new(0), Condvar::new()));
let data_clone = data.clone();
// 创建多个等待线程
let mut handles = vec![];
for _ in 0..10 {
let data = data_clone.clone();
let handle = thread::spawn(move || {
let (lock, cvar) = &*data;
let mut num = lock.lock().unwrap();
while *num < 10 {
num = cvar.wait(num).unwrap();
// 处理虚假唤醒
if *num < 10 {
continue;
}
println!("Thread woke up with num: {}", num);
}
});
handles.push(handle);
}
// 主线程修改数据并通知
let (lock, cvar) = &*data;
let mut num = lock.lock().unwrap();
*num = 10;
cvar.notify_all();
for handle in handles {
handle.join().unwrap();
}
}
在这个示例中,通过Condvar
和Mutex
的配合,实现了线程间的同步。并且在等待条件变量唤醒后,再次检查条件以处理虚假唤醒的情况。如果有更复杂的场景,可以根据上述优化策略,例如使用RwLock
来进一步优化锁的使用,减少锁竞争。