MST

星途 面试题库

面试题:Rust条件变量在复杂场景下的应用

假设有一个生产者 - 消费者模型,生产者线程生成数据并放入一个共享队列,消费者线程从队列中取出数据进行处理。要求使用Rust的条件变量和互斥锁实现该模型,并且需要处理队列满和队列空的情况,防止线程饥饿和死锁。请写出完整的代码实现,并解释关键步骤。
49.9万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试
use std::sync::{Arc, Condvar, Mutex};
use std::thread;
use std::time::Duration;

// 定义共享队列的最大容量
const QUEUE_CAPACITY: usize = 5;

fn main() {
    let queue = Arc::new((Mutex::new(Vec::with_capacity(QUEUE_CAPACITY)), Condvar::new()));
    let producer_queue = queue.clone();
    let consumer_queue = queue.clone();

    // 创建生产者线程
    let producer = thread::spawn(move || {
        let (lock, cvar) = &*producer_queue;
        let mut data = 0;

        loop {
            let mut queue = lock.lock().unwrap();

            while queue.len() == QUEUE_CAPACITY {
                queue = cvar.wait(queue).unwrap();
            }

            queue.push(data);
            println!("Produced: {}", data);
            data += 1;

            cvar.notify_one();
        }
    });

    // 创建消费者线程
    let consumer = thread::spawn(move || {
        let (lock, cvar) = &*consumer_queue;

        loop {
            let mut queue = lock.lock().unwrap();

            while queue.is_empty() {
                queue = cvar.wait(queue).unwrap();
            }

            let item = queue.remove(0);
            println!("Consumed: {}", item);

            cvar.notify_one();

            // 模拟处理数据的时间
            thread::sleep(Duration::from_millis(100));
        }
    });

    // 等待线程结束(实际上这里的线程不会结束,只是为了防止主线程提前退出)
    producer.join().unwrap();
    consumer.join().unwrap();
}

关键步骤解释:

  1. 定义共享队列和条件变量
    • 使用 Arc<(Mutex<Vec<T>>, Condvar)> 来创建一个线程安全的共享队列和条件变量。Arc 用于在多个线程间共享所有权,Mutex 用于保护共享队列,Condvar 用于线程间的同步。
    • QUEUE_CAPACITY 定义了共享队列的最大容量。
  2. 生产者线程
    • 获取锁并进入临界区,使用 lock.lock().unwrap()
    • 当队列满时(queue.len() == QUEUE_CAPACITY),调用 cvar.wait(queue).unwrap() 释放锁并等待条件变量通知。这一步保证了生产者线程不会一直占用锁,避免死锁和线程饥饿。
    • 生成数据并放入队列,然后使用 cvar.notify_one() 通知等待的消费者线程。
  3. 消费者线程
    • 同样获取锁并进入临界区。
    • 当队列空时(queue.is_empty()),调用 cvar.wait(queue).unwrap() 释放锁并等待条件变量通知。
    • 从队列中取出数据并处理,然后使用 cvar.notify_one() 通知等待的生产者线程。
  4. 线程创建与等待
    • 使用 thread::spawn 创建生产者和消费者线程。
    • 使用 join 方法等待线程结束(在实际应用中,这些线程可能是长期运行的,这里只是为了防止主线程提前退出)。