You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于C++阻塞队列中push与pop线程未交替执行的疑问

关于C++阻塞队列中push与pop线程未交替执行的疑问

嘿,我完全懂你的困惑——你本来以为push线程把元素塞进队列、解锁互斥量并通知条件变量后,pop线程会立刻醒过来执行,但结果却是push一口气把队列填满,之后pop才开始清空队列,完全没按你预想的“交替执行”来,对吧?咱们来掰扯清楚背后的原因。

首先要明确一个核心点:条件变量的notify只是“唤醒等待的线程”,但它管不了操作系统的线程调度器。

问题出在哪?

  1. 线程调度的“惰性”:当push线程调用notify_one()之后,它并没有立刻放弃CPU控制权。操作系统的调度器通常会让当前正在运行的线程继续执行,直到它的时间片用完,或者遇到阻塞操作(比如wait)。如果你的push线程是在一个循环里不断调用push函数,那它解锁mutex后,很可能会立刻再次尝试获取mutex——因为它就在现场,调度器大概率会把mutex再次分配给它,而不是切换到刚被唤醒的pop线程。

  2. 被唤醒的pop线程还要抢mutex:就算pop线程被notify唤醒了,它也得先拿到mutex才能访问队列。但这时候push线程可能已经重新抢到了mutex,继续往队列里塞元素,直到队列被填满,push线程触发wait条件(队列满了),这时候它才会释放mutex并进入阻塞状态,调度器才会切换到pop线程开始工作。

  3. 可能你的代码里pop后没notify?(不过从你的描述看,主要问题是push没让pop有机会执行,但还是提一句)如果pop线程取出元素后没有notify push线程,那当队列被清空后,pop线程会阻塞,而push线程可能还在等notify——不过这是另一种情况了。

怎么让它们更可能交替执行?

你没法强迫调度器严格交替,但可以通过一些操作增加交替的概率:

  • 在push/pop后主动让出CPU:调用std::this_thread::yield(),告诉调度器“我现在可以歇会,让别的线程跑吧”。这会大大提高pop线程被调度的概率,但也不是100%保证。
  • 确保push和pop都正确调用notify:push完元素要notify等待的pop线程,pop完元素要notify等待的push线程,这是阻塞队列的基本操作,保证两边都能及时收到信号。

修正后的代码示例

给你贴个完整的阻塞队列实现,加上了yield和正确的notify逻辑:

#include <iostream>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>

using namespace std;

template<typename E>
class BlockingQueue
{
private:
    size_t mMaxSize;
    queue<E> mQueue;
    mutex mtx;
    condition_variable mConditionVar;

public:
    BlockingQueue(size_t size): mMaxSize{size} {}

    void push(E element)
    {
        unique_lock<mutex> pushLock(mtx);
        // 等待队列有空闲位置
        mConditionVar.wait(pushLock, [this](){ return mQueue.size() < mMaxSize; });
        mQueue.push(element);
        cout << "Pushed: " << element << endl;
        // 提前解锁(可选,unique_lock离开作用域也会自动解锁)
        pushLock.unlock();
        // 通知pop线程有新元素了
        mConditionVar.notify_one();
        // 主动让出CPU
        this_thread::yield();
    }

    E pop()
    {
        unique_lock<mutex> popLock(mtx);
        // 等待队列非空
        mConditionVar.wait(popLock, [this](){ return !mQueue.empty(); });
        E element = mQueue.front();
        mQueue.pop();
        cout << "Popped: " << element << endl;
        popLock.unlock();
        // 通知push线程有空位了
        mConditionVar.notify_one();
        this_thread::yield();
        return element;
    }
};

int main()
{
    BlockingQueue<int> bq(5);

    thread pushThread([&bq](){
        for(int i=0; i<10; ++i){
            bq.push(i);
        }
    });

    thread popThread([&bq](){
        for(int i=0; i<10; ++i){
            bq.pop();
        }
    });

    pushThread.join();
    popThread.join();

    return 0;
}

运行这个代码,你会看到push和pop交替执行的概率大大提高——不过还是要记住,线程调度的行为是不确定的,极端情况下还是可能出现连续push或连续pop的情况,这是操作系统的特性,没法完全避免。

备注:内容来源于stack exchange,提问作者Rudransh Srivastava

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.16 07:13:07