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

C++多线程中锁与条件变量的异常阻塞问题

分析与修复你的C++多线程同步问题

我来帮你拆解下这个多线程同步的问题,你的代码里有几个关键逻辑错误导致了程序卡住和CPU高占用,我们一步步梳理并修复:

核心问题点

1. 解锁后调用cv.wait触发未定义行为

在main函数中,你先执行了lk.unlock(),之后直接用这个已经解锁的unique_lock调用cv.wait(lk, ...)。condition_variable::wait要求传入的锁必须处于锁定状态,解锁状态下调用会触发未定义行为——这就是为什么偶尔加个cout就“正常”了,那只是竞争条件下的偶然巧合,不是真的解决了问题。

2. 循环notify_one导致CPU空转

比如T1里的while (dataz) cv.notify_one();和main里的while (datax) cv.notify_one();,这种循环完全没必要:条件变量的通知只需要触发一次,被唤醒的线程会自行检查条件是否满足,循环通知只会让CPU无意义空转(这就是你看到CPU占用率高的原因)。

3. 状态变量的逻辑闭环混乱

原代码里的datax、dataz、zPrinted三个状态变量的切换逻辑不够清晰,且部分场景下可能出现数据竞争,导致线程读取到脏状态。

修复后的代码

#include "stdafx.h"
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
using namespace std;

mutex m;
condition_variable cv;
bool x_ready = false;    // 标记main已输入x,T1可计算
bool z_calc_done = false;// 标记T1已算出z,T2可打印
bool z_print_done = false;// 标记T2已打印z,main可继续输入

void T1(const int& x, int& z) {
    while (true) {
        unique_lock<mutex> lk(m);
        // 等待main输入完成并标记x_ready
        cv.wait(lk, []{ return x_ready; });
        
        z = x * x;
        x_ready = false;
        z_calc_done = true;
        lk.unlock();
        cv.notify_one(); // 通知T2可以打印z
    }
}

void T2(const int& z) {
    while (true) {
        unique_lock<mutex> lk(m);
        // 等待T1完成计算并标记z_calc_done
        cv.wait(lk, []{ return z_calc_done; });
        
        cout << "z = " << z << endl;
        z_calc_done = false;
        z_print_done = true;
        lk.unlock();
        cv.notify_one(); // 通知main可以继续输入
    }
}

int main() {
    int x, z;
    char c;
    thread threadT1(T1, cref(x), ref(z));
    thread threadT2(T2, cref(z));

    do {
        unique_lock<mutex> lk(m);
        cout << "Enter x: ";
        cin >> x;
        x_ready = true;
        lk.unlock();
        cv.notify_one(); // 通知T1开始计算

        // 等待T2打印完成,确保wait时锁处于锁定状态
        unique_lock<mutex> wait_lk(m);
        cv.wait(wait_lk, []{ return z_print_done; });
        z_print_done = false;

        cout << "Continue? (y/n): ";
        cin >> c;
    } while (c == 'y');

    // 实际场景中需添加线程终止逻辑,这里暂时用detach简化
    threadT1.detach();
    threadT2.detach();
    return 0;
}

关键改动说明

  1. 状态变量重命名:用x_ready、z_calc_done、z_print_done明确每个状态的含义,避免逻辑混淆。
  2. 移除无意义的循环通知:每个状态变更后只调用一次cv.notify_one(),足够唤醒等待的线程。
  3. 确保wait时锁的状态正确:main函数中等待打印完成时,重新创建了一个锁定的unique_lock,彻底避免未定义行为。
  4. 梳理逻辑闭环:
    • main输入x → 标记x_ready → 通知T1
    • T1计算z → 标记z_calc_done → 通知T2
    • T2打印z → 标记z_print_done → 通知main
    • main等待z_print_done后继续循环

这样整个同步逻辑清晰可控,不会出现死锁或CPU空转的问题。

内容的提问来源于stack exchange,提问作者C. Cappello

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:13:58