实现思路
- 互斥锁(Mutex):用于保护共享资源,确保同一时间只有一个线程可以访问共享资源。在生产者 - 消费者模型中,共享资源可能是一个缓冲区。
- 条件变量(Condvar):用于线程间的通信。生产者线程在缓冲区有空间时通知消费者线程,消费者线程在缓冲区有数据时通知生产者线程。
- 生产者:生产数据,获取互斥锁,将数据放入共享缓冲区,然后通知等待的消费者线程。
- 消费者:获取互斥锁,检查共享缓冲区是否有数据,若没有则等待条件变量通知,有数据则取出并消费。
关键代码片段
use std::sync::{Arc, Mutex, Condvar};
use std::thread;
fn main() {
let shared_data = Arc::new((Mutex::new(Vec::new()), Condvar::new()));
let producer_shared = shared_data.clone();
let consumer_shared = shared_data.clone();
let producer = thread::spawn(move || {
for i in 0..10 {
let (lock, cvar) = &*producer_shared;
let mut data = lock.lock().unwrap();
data.push(i);
println!("Produced: {}", i);
drop(data);
cvar.notify_one();
}
});
let consumer = thread::spawn(move || {
let (lock, cvar) = &*consumer_shared;
for _ in 0..10 {
let mut data = lock.lock().unwrap();
while data.is_empty() {
data = cvar.wait(data).unwrap();
}
let item = data.pop().unwrap();
println!("Consumed: {}", item);
}
});
producer.join().unwrap();
consumer.join().unwrap();
}
处理死锁问题
- 按顺序获取锁:确保所有线程以相同顺序获取锁,避免循环依赖。在生产者 - 消费者模型中,只有一个互斥锁,所以这方面不容易出现死锁,但如果有多个锁(例如多个缓冲区),要注意获取顺序。
- 避免嵌套锁:尽量减少锁的嵌套使用,以降低死锁风险。
- 使用超时机制:在获取锁时设置超时,若在规定时间内未获取到锁,则放弃操作并释放已获取的锁,如使用
try_lock
方法并结合std::time::Duration
设置超时。
处理虚假唤醒问题
- 使用循环检查条件:在消费者线程等待条件变量通知后,使用循环再次检查共享缓冲区是否真的有数据,而不是直接假设通知一定意味着缓冲区有数据。如上述代码中
while data.is_empty() { data = cvar.wait(data).unwrap(); }
,这样即使出现虚假唤醒,也会再次等待直到条件满足。