条件变量死锁问题排查与修正请求(附C++代码示例)
问题分析与修复方案
死锁发生的位置
死锁的核心原因有两点:
turnOff()未唤醒条件变量:调用turnOff()将flag设为false后,没有通知等待在条件变量cv上的消费者线程。如果此时消费者正卡在pop()方法的cv.wait()中,即使flag已经变为false,线程也不会被唤醒,会一直阻塞,最终导致main函数的consumerThread.join()无法完成,形成死锁。- 消费者循环逻辑错误:
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
相关产品推荐
相关产品推荐

