C++多线程程序随机崩溃/死锁问题排查求助
我用std::thread写了下面这个C多线程程序,原本想实现主线程给子线程分配任务,等所有子线程做完后主线程汇总计算的交替执行逻辑,但运行几次迭代后程序会随机崩溃或者死锁。我的运行环境是Ubuntu 22.04、G 11.4。
#include <iostream> #include <thread> #include <mutex> #include <condition_variable> using namespace std; const int threadNum = 4; mutex mt[threadNum]; condition_variable cv[threadNum]; thread threadList[threadNum]; bool threadWork[threadNum]; void work(int id) { while (true) { unique_lock<mutex> lck(mt[id]); cv[id].wait(lck, [&]() { return threadWork[id]; }); // 等待任务 // 执行任务逻辑 threadWork[id] = false; cv[id].notify_all(); } } int main() { for (int i = 0; i < threadNum; i ++) { threadWork[i] = false; threadList[i] = thread(work, i); } while (true) { for (int i = 0; i < threadNum; i ++) { // 给每个线程分配任务 threadWork[i] = true; cv[i].notify_all(); } for (int i = 0; i < threadNum; i ++) { // 等待所有线程完成任务 unique_lock<mutex> lck(mt[i]); cv[i].wait(lck, [&]() { return !threadWork[i]; }); cout << "Finish thread " << i << endl; } // 主线程汇总逻辑 } for (int i = 0; i < threadNum; i ++) threadList[i].join(); return 0; }
主要问题
1. threadWork变量存在数据竞争
threadWork是普通bool数组,主线程分配任务时直接在无锁状态下修改threadWork[i] = true,而子线程是在持有mt[i]锁的情况下修改threadWork[i] = false。这种跨线程的无同步读写属于数据竞争,C++标准里这种情况会导致未定义行为——程序可能随机崩溃、死锁,或者出现逻辑混乱,完全看运气。
2. 条件变量通知可能被“错过”
主线程设置threadWork[i] = true后直接调用notify_all(),但这个过程没拿对应线程的mt[i]锁,很容易出现时序问题:比如子线程刚做完任务把threadWork[i]设为false,主线程同时把它设为true并发通知,但子线程还没进入下一轮等待,这个通知就白发了;或者子线程准备进入wait前,主线程已经把threadWork[i]设为true,等子线程进入wait时,主线程可能又修改了状态,导致子线程一直等下去,触发死锁。
3. 标准输出没有同步
主线程循环里直接用cout输出,虽然cout不会让不同线程的输出字符乱序,但多个线程(包括主线程)同时输出时,整体的输出顺序会混乱,这虽然不是崩溃的直接原因,但属于线程程序的不良写法。
修复方法
方法一:用原子变量替代普通bool
把bool threadWork[threadNum];改成std::atomic_bool threadWork[threadNum];,原子变量的读写操作天然是线程安全的,能彻底避免数据竞争:
#include <atomic> // ... std::atomic_bool threadWork[threadNum];
方法二:读写threadWork时都持有对应锁
主线程分配任务的时候,先获取对应线程的锁再修改threadWork,保证状态修改的同步性:
// 主线程分配任务的循环改成这样: for (int i = 0; i < threadNum; i ++) { unique_lock<mutex> lck(mt[i]); threadWork[i] = true; cv[i].notify_all(); }
同步标准输出
加个全局互斥锁来保护cout,避免输出混乱:
mutex cout_mtx; // ... // 主线程输出的时候改成: { lock_guard<mutex> lck(cout_mtx); cout << "Finish thread " << i << endl; }
内容的提问来源于stack exchange,提问作者Recursion

