代码实现思路
- 共享队列:使用
std::sync::Mutex
来保护共享队列,确保线程安全访问。例如,定义一个Mutex<Vec<T>>
,T
为队列中数据的类型。
- 条件变量:使用
std::sync::Condvar
来通知消费者线程有新数据可用。
- 生产者线程:
- 生成数据。
- 锁定
Mutex
,将数据放入共享队列。
- 解锁
Mutex
后,通过Condvar
通知等待的消费者线程。
- 消费者线程:
- 循环等待条件变量通知。
- 当收到通知,锁定
Mutex
,从共享队列取出数据。
- 解锁
Mutex
并处理数据。
示例代码
use std::sync::{Arc, Condvar, Mutex};
use std::thread;
fn main() {
let shared_queue = Arc::new((Mutex::new(Vec::new()), Condvar::new()));
let producer_shared_queue = shared_queue.clone();
let consumer_shared_queue = shared_queue.clone();
let producer = thread::spawn(move || {
for i in 0..10 {
let (lock, cvar) = &*producer_shared_queue;
let mut queue = lock.lock().unwrap();
queue.push(i);
drop(queue);
cvar.notify_one();
}
});
let consumer = thread::spawn(move || {
let (lock, cvar) = &*consumer_shared_queue;
let mut queue = lock.lock().unwrap();
while queue.is_empty() {
queue = cvar.wait(queue).unwrap();
}
let data = queue.pop().unwrap();
println!("Consumed: {}", data);
});
producer.join().unwrap();
consumer.join().unwrap();
}
可能遇到的死锁情况
- 双重锁定:如果消费者线程在等待条件变量通知时一直持有
Mutex
锁,而生产者线程需要获取Mutex
锁来放入数据并通知消费者,就会导致死锁。因为生产者无法获取锁,消费者也不会释放锁(因为它在等待通知)。
- 通知丢失:如果生产者在消费者等待条件变量之前就发出通知,而消费者线程没有正确处理这种情况(例如没有循环等待),可能会导致消费者永远等待通知,而生产者继续生产数据,最终导致死锁(例如共享队列满了之后生产者等待队列有空间)。
避免死锁的方法
- 正确释放锁:消费者线程在等待条件变量通知时,应释放
Mutex
锁,让生产者线程能够获取锁放入数据并通知。在Rust中,Condvar::wait
方法会自动释放传入的锁,并在被唤醒后重新获取锁。
- 循环等待:消费者线程应该使用循环来等待条件变量通知,确保即使通知丢失,也能继续等待新的通知。例如在
while queue.is_empty()
循环中调用cvar.wait
。