实现一个线程安全的阻塞队列

本文详细介绍了ArrayBlockingQueue的两种同步实现方式:一种是使用synchronized关键字,通过wait和notifyAll进行线程间的同步;另一种是使用ReentrantLock配合Condition,更加精确地控制线程的唤醒,提高效率。

 1、使用synchronized实现。由于synchronized是同一把锁,所以使用notify()可能会唤醒非目标线程,notifyAll()唤醒全部线程则会带来大量的 CPU 上下文切换和锁竞争

public class ArrayBlockingQueue {
    private Object[] array; //数组
    private int head; //头
    private int tail; //尾
    private volatile int size; //元素个数

    public ArrayBlockingQueue(int capacity) {
        this.array = new Object[capacity];
    }

    //写入元素
    public synchronized void put(Object o) throws InterruptedException {
        //当队列满时,阻塞
        while (size == array.length) {
            this.wait();
        }
        array[tail++] = o;
        if (tail == array.length) {
            tail = 0;
        }
        size ++;
        //唤醒线程
        this.notifyAll();
    }

    //取出元素
    public synchronized Object get() throws InterruptedException {
        //当队列为空,阻塞
        while (size == 0) {
            this.wait();
        }
        Object o = array[head++];
        if (head == array.length) {
            head = 0;
        }
        size --;
        //唤醒线程
        this.notifyAll();
        return o;
    }

}

2、使用ReentrantLock,可以用Condition指定要唤醒的线程,所以效率更高

public class ArrayBlockingQueue {
    private Object[] array; //数组
    private int head; //头
    private int tail; //尾
    private volatile int size; //元素个数
    private ReentrantLock lock = new ReentrantLock(); //锁
    private Condition notEmpty = lock.newCondition(); //非空
    private Condition notFull = lock.newCondition();  //非满

    public ArrayBlockingQueue(int capacity) {
        this.array = new Object[capacity];
    }

    //写入元素
    public void put(Object o) throws InterruptedException {
        try{
            lock.lock();
            //当队列满时,阻塞
            while (size == array.length) {
                notFull.wait();
            }
            array[tail++] = o;
            if (tail == array.length) {
                tail = 0;
            }
            size ++;
            //唤醒线程
            notEmpty.notifyAll();
        } finally {
            lock.unlock();
        }
    }

    //取出元素
    public Object get() throws InterruptedException {
        lock.lock();
        try {
            //当队列为空,阻塞
            while (size == 0) {
                notEmpty.wait();
            }
            Object o = array[head++];
            if (head == array.length) {
                head = 0;
            }
            size --;
            //唤醒线程
            notFull.notifyAll();
            return o;
        } finally {
            lock.unlock();
        }
    }

}

 

<think>嗯,用户问的是如何在C++中实现线程安全的队列。我需要先回想一下线程安全的基本概念。线程安全通常涉及到互斥锁和条件变量的使用,确保多个线程访问共享资源时不会发生数据竞争或条件竞争。 首先,用户可能需要一个队列结构,支持多线程下的安全入队和出队操作。基本的实现应该包括一个std::queue作为底层容器,然后用std::mutex来保护队列的访问。同时,当队列为空时,出队操作可能需要等待,直到有元素被添加进来,这时候就需要用到std::condition_variable。 接着,我需要考虑异常安全的问题。比如,在入队时如果分配内存失败,是否会影响锁的状态。通常,锁的管理应该使用RAII方式,比如std::lock_guard或std::unique_lock,确保在异常发生时锁能被正确释放。 然后,用户可能关心如何优雅地关闭队列,特别是在消费者线程需要退出时。可以添加一个标志位,比如stop_requested,当设置该标志时,通知所有等待的条件变量,让线程能够退出循环。 另外,性能优化也是一个方面。比如,使用std::condition_variable的notify_one或notify_all方法,在适当的时候唤醒等待的线程,避免不必要的唤醒。此外,移动语义或者emplace方法可以减少拷贝开销,提高性能。 用户提供的引用中有一个SimpleTaskQueue类,它使用了线程和任务队列,但似乎没有处理线程安全的问题。因此,在示例代码中需要展示如何将互斥锁和条件变量集成进去。 可能还需要考虑队列的大小限制,即实现有界队列,防止生产者生产过快导致内存耗尽。这可以通过在push操作中添加等待条件,直到队列大小小于某个阈值。 最后,需要给出一个完整的示例代码,展示如何定义线程安全的队列类,并在生产者和消费者线程中使用它。同时,解释代码中的关键部分,比如锁的作用域、条件变量的使用,以及如何安全地终止线程。 总结步骤: 1. 定义线程安全队列类,包含队列、互斥锁、条件变量。 2. 实现push和pop方法,使用锁保护操作,条件变量进行等待和通知。 3. 添加停止机制,允许优雅退出。 4. 处理异常和资源管理,确保锁的释放。 5. 提供示例代码,展示生产者和消费者的使用。</think>以下是C++实现线程安全队列的解决方案及示例代码,包含核心实现要点和完整示例: ### 一、线程安全队列实现要点 1. **互斥锁保护**:使用$std::mutex$保护队列的读写操作 2. **条件变量通知**:通过$std::condition_variable$实现生产者-消费者模式 3. **异常安全**:使用RAII方式管理锁($std::lock_guard$/$std::unique_lock$) 4. **优雅终止**:设置停止标志位$stop_requested$并通知所有等待线程[^2] ### 二、完整实现示例 ```cpp #include <queue> #include <mutex> #include <condition_variable> #include <memory> template<typename T> class ThreadSafeQueue { public: ThreadSafeQueue() = default; ~ThreadSafeQueue() { stop(); } // 推入数据(支持移动语义) void push(T value) { std::lock_guard<std::mutex> lock(m_mutex); m_queue.push(std::move(value)); m_cond.notify_one(); } // 尝试弹出数据(立即返回) bool try_pop(T& value) { std::lock_guard<std::mutex> lock(m_mutex); if (m_queue.empty() || m_stop_requested) return false; value = std::move(m_queue.front()); m_queue.pop(); return true; } // 阻塞式弹出数据(支持超时) template<typename Duration> bool wait_and_pop(T& value, Duration timeout) { std::unique_lock<std::mutex> lock(m_mutex); if (!m_cond.wait_for(lock, timeout, [this]{ return !m_queue.empty() || m_stop_requested; })) return false; if (m_stop_requested) return false; value = std::move(m_queue.front()); m_queue.pop(); return true; } // 停止队列操作 void stop() { { std::lock_guard<std::mutex> lock(m_mutex); m_stop_requested = true; } m_cond.notify_all(); } bool empty() const { std::lock_guard<std::mutex> lock(m_mutex); return m_queue.empty(); } private: mutable std::mutex m_mutex; std::queue<T> m_queue; std::condition_variable m_cond; bool m_stop_requested = false; }; ``` ### 三、代码解析 1. **并发控制**: - 使用$std::mutex$确保队列操作的原子性 - $std::condition_variable$实现高效等待/通知机制 - 通过$std::unique_lock$实现条件变量的带锁等待 2. **接口设计**: - $push()$:支持移动语义减少拷贝开销 - $try_pop()$:非阻塞式访问 - $wait_and_pop()$:支持超时等待 - $stop()$:安全终止所有等待线程 3. **使用示例**: ```cpp // 生产者线程 void producer(ThreadSafeQueue<int>& queue) { for (int i = 0; i < 10; ++i) { queue.push(i); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } // 消费者线程 void consumer(ThreadSafeQueue<int>& queue) { int value; while (queue.wait_and_pop(value, std::chrono::seconds(1))) { std::cout << "Received: " << value << std::endl; } } ``` ### 四、优化建议 1. **批量操作**:添加$push_bulk()$接口支持批量插入 2. **容量限制**:添加最大容量限制防止内存耗尽 3. **性能优化**:使用无锁队列结构替代互斥锁(需配合原子操作) 4. **异常处理**:完善$push()$操作的内存异常处理机制[^1]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值