实现阻塞队列
字数 799 2025-11-05 23:47:54

实现阻塞队列

题目描述
阻塞队列是一种特殊的队列,当队列为空时,从队列获取元素的操作会被阻塞,直到队列中有新元素;当队列已满时,向队列添加元素的操作会被阻塞,直到队列中有空闲位置。请实现一个线程安全的阻塞队列。

知识背景
阻塞队列是多线程编程中的重要同步工具,常用于生产者-消费者模型。它通过内部的条件变量和互斥锁来实现线程间的协调。

实现步骤

  1. 基本结构设计
    我们需要一个队列容器(如数组或链表)、一个互斥锁(保证线程安全)和两个条件变量(分别对应队列空和队列满的条件)。

  2. 核心操作分析

  • put(element):向队列添加元素。如果队列已满,则阻塞调用线程直到有空位。
  • take():从队列取出元素。如果队列为空,则阻塞调用线程直到有新元素。
  1. 详细实现过程

步骤1:定义类成员

#include <queue>
#include <mutex>
#include <condition_variable>

template<typename T>
class BlockingQueue {
private:
    std::queue<T> queue_;          // 底层队列容器
    std::mutex mutex_;             // 互斥锁
    std::condition_variable not_empty_;  // 非空条件变量
    std::condition_variable not_full_;   // 非满条件变量
    size_t capacity_;              // 队列容量

这里使用标准库的queue作为底层容器,通过mutex实现互斥访问,两个condition_variable分别用于等待非空和非满条件。

步骤2:构造函数初始化

public:
    explicit BlockingQueue(size_t capacity) : capacity_(capacity) {}

构造函数接收一个容量参数,用于指定队列的最大长度。

步骤3:实现put方法

    void put(const T& item) {
        std::unique_lock<std::mutex> lock(mutex_);
        
        // 等待队列非满(条件变量的标准用法:循环检查防止虚假唤醒)
        not_full_.wait(lock, [this]() { 
            return queue_.size() < capacity_; 
        });
        
        queue_.push(item);          // 添加元素到队列
        not_empty_.notify_one();    // 通知可能等待的消费者
    }
  • 获取互斥锁后,检查队列是否已满
  • 如果已满,则通过wait释放锁并阻塞,直到被其他线程唤醒
  • 添加元素后,通过notify_one唤醒一个可能等待的消费者线程

步骤4:实现take方法

    T take() {
        std::unique_lock<std::mutex> lock(mutex_);
        
        // 等待队列非空
        not_empty_.wait(lock, [this]() { 
            return !queue_.empty(); 
        });
        
        T item = queue_.front();    // 获取队首元素
        queue_.pop();               // 移除队首元素
        not_full_.notify_one();     // 通知可能等待的生产者
        return item;
    }
  • 与put方法对称,先等待队列非空
  • 取出元素后通知可能阻塞的生产者线程

步骤5:可选辅助方法

    bool empty() const {
        std::lock_guard<std::mutex> lock(mutex_);
        return queue_.empty();
    }
    
    size_t size() const {
        std::lock_guard<std::mutex> lock(mutex_);
        return queue_.size();
    }

这些辅助方法也需要加锁保证线程安全。

关键要点

  1. 条件变量使用模式:总是使用while循环(或lambda表达式)检查条件,防止虚假唤醒
  2. RAII锁管理:使用unique_lock自动管理锁的获取和释放
  3. 通知时机:在改变条件状态后立即通知等待的线程
  4. 异常安全:确保在异常情况下锁能被正确释放

这个实现提供了阻塞队列的核心功能,可以安全地在多线程环境下使用,是理解线程同步的经典案例。

实现阻塞队列 题目描述 阻塞队列是一种特殊的队列,当队列为空时,从队列获取元素的操作会被阻塞,直到队列中有新元素;当队列已满时,向队列添加元素的操作会被阻塞,直到队列中有空闲位置。请实现一个线程安全的阻塞队列。 知识背景 阻塞队列是多线程编程中的重要同步工具,常用于生产者-消费者模型。它通过内部的条件变量和互斥锁来实现线程间的协调。 实现步骤 基本结构设计 我们需要一个队列容器(如数组或链表)、一个互斥锁(保证线程安全)和两个条件变量(分别对应队列空和队列满的条件)。 核心操作分析 put(element) :向队列添加元素。如果队列已满,则阻塞调用线程直到有空位。 take() :从队列取出元素。如果队列为空,则阻塞调用线程直到有新元素。 详细实现过程 步骤1:定义类成员 这里使用标准库的queue作为底层容器,通过mutex实现互斥访问,两个condition_ variable分别用于等待非空和非满条件。 步骤2:构造函数初始化 构造函数接收一个容量参数,用于指定队列的最大长度。 步骤3:实现put方法 获取互斥锁后,检查队列是否已满 如果已满,则通过wait释放锁并阻塞,直到被其他线程唤醒 添加元素后,通过notify_ one唤醒一个可能等待的消费者线程 步骤4:实现take方法 与put方法对称,先等待队列非空 取出元素后通知可能阻塞的生产者线程 步骤5:可选辅助方法 这些辅助方法也需要加锁保证线程安全。 关键要点 条件变量使用模式 :总是使用while循环(或lambda表达式)检查条件,防止虚假唤醒 RAII锁管理 :使用unique_ lock自动管理锁的获取和释放 通知时机 :在改变条件状态后立即通知等待的线程 异常安全 :确保在异常情况下锁能被正确释放 这个实现提供了阻塞队列的核心功能,可以安全地在多线程环境下使用,是理解线程同步的经典案例。