MST
星途 面试题库

面试题:Rust条件变量在生产者 - 消费者模型中的应用

在一个基于Rust的生产者 - 消费者模型中,使用条件变量实现线程间的同步,生产者线程不断生成数据并放入共享队列,消费者线程从共享队列取出数据进行处理。请详细描述代码实现思路,并指出可能遇到的死锁情况以及如何避免。
21.9万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

代码实现思路

  1. 共享队列:使用std::sync::Mutex来保护共享队列,确保线程安全访问。例如,定义一个Mutex<Vec<T>>T为队列中数据的类型。
  2. 条件变量:使用std::sync::Condvar来通知消费者线程有新数据可用。
  3. 生产者线程
    • 生成数据。
    • 锁定Mutex,将数据放入共享队列。
    • 解锁Mutex后,通过Condvar通知等待的消费者线程。
  4. 消费者线程
    • 循环等待条件变量通知。
    • 当收到通知,锁定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();
}

可能遇到的死锁情况

  1. 双重锁定:如果消费者线程在等待条件变量通知时一直持有Mutex锁,而生产者线程需要获取Mutex锁来放入数据并通知消费者,就会导致死锁。因为生产者无法获取锁,消费者也不会释放锁(因为它在等待通知)。
  2. 通知丢失:如果生产者在消费者等待条件变量之前就发出通知,而消费者线程没有正确处理这种情况(例如没有循环等待),可能会导致消费者永远等待通知,而生产者继续生产数据,最终导致死锁(例如共享队列满了之后生产者等待队列有空间)。

避免死锁的方法

  1. 正确释放锁:消费者线程在等待条件变量通知时,应释放Mutex锁,让生产者线程能够获取锁放入数据并通知。在Rust中,Condvar::wait方法会自动释放传入的锁,并在被唤醒后重新获取锁。
  2. 循环等待:消费者线程应该使用循环来等待条件变量通知,确保即使通知丢失,也能继续等待新的通知。例如在while queue.is_empty()循环中调用cvar.wait