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

条件变量死锁问题排查与修正请求(附C++代码示例)

问题分析与修复方案

死锁发生的位置

死锁的核心原因有两点:

  1. turnOff()未唤醒条件变量:调用turnOff()将flag设为false后,没有通知等待在条件变量cv上的消费者线程。如果此时消费者正卡在pop()方法的cv.wait()中,即使flag已经变为false,线程也不会被唤醒,会一直阻塞,最终导致main函数的consumerThread.join()无法完成,形成死锁。
  2. 消费者循环逻辑错误:consumerLoop的while(q.isFlagOn())判断会在flag关闭后直接退出循环,但此时队列中可能还有未消费的元素;而且如果线程已经进入pop()的等待状态,这个循环判断根本不会执行,无法触发退出。

修正后的代码

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

class SafeQueue
{
private:
    std::queue<int> data_queue;
    mutable std::mutex m;
    std::condition_variable cv;
    std::atomic<bool> flag{ true }; // 控制队列运行的原子标志

public:
    void push(int val)
    {
        std::lock_guard<std::mutex> lock(m);
        data_queue.push(val);
        cv.notify_one(); // 推送数据后通知等待线程
    }

    bool pop(int& val)
    {
        std::unique_lock<std::mutex> lock(m);
        // 等待直到队列非空,或者标志关闭
        cv.wait(lock, [this]() { return !data_queue.empty() || !flag; });

        // 标志关闭且队列为空,返回false表示终止
        if (!flag && data_queue.empty())
        {
            return false;
        }

        // 队列有数据则取出
        if (!data_queue.empty())
        {
            val = data_queue.front();
            data_queue.pop();
            return true;
        }
        return false;
    }

    void turnOff()
    {
        flag = false;
        cv.notify_all(); // 关闭标志后,唤醒所有等待的线程
    }
};

void consumerLoop(SafeQueue& q)
{
    int val;
    // 以pop的返回值作为循环终止条件,确保消费完所有剩余元素
    while (q.pop(val))
    {
        std::cout << "Consumed: " << val << std::endl;
    }
    std::cout << "Consumer exiting" << std::endl;
}

int main()
{
    SafeQueue q;
    std::thread consumerThread(consumerLoop, std::ref(q));

    // 生产者推送数据
    for (int i = 0; i < 10; ++i)
    {
        q.push(i);
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }

    // 关闭队列标志
    q.turnOff();

    consumerThread.join();

    return 0;
}

关键修复点说明

  • turnOff()添加cv.notify_all():确保在关闭标志后,所有等待在条件变量上的线程都能被唤醒,检查退出条件。
  • 重构消费者循环逻辑:不再直接判断flag,而是以pop()的返回值作为循环终止条件。这样既保证了队列中所有元素都被消费完毕,又能在标志关闭且队列为空时正确退出。

其他实现建议

  • 替换为std::deque:只需将std::queue<int>改为std::deque<int>即可,queue本身默认就是用deque作为底层容器,替换后可以直接使用deque的所有特性(比如两端操作)。
  • 考虑批量操作:如果需要处理大量数据,可以添加批量推送/弹出的方法,减少锁的竞争次数,提升性能。
  • 异常安全:在pop()中如果涉及复杂类型,要确保元素拷贝/移动的异常安全性,避免锁持有期间抛出异常导致资源泄漏。
  • 使用std::jthread(C++20+):std::jthread会自动join,无需手动调用,简化线程管理。

内容的提问来源于stack exchange,提问作者SpeakX

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 05:42:07