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

C++多线程程序随机崩溃/死锁问题排查求助

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 11:47:11