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

移动printf至count变量增减操作前后引发的线程异常行为排查

线程同步问题解析:为何printf位置会影响程序运行

核心问题:未正确保护共享变量count

你的代码存在严重的竞态条件:作为线程同步标志的count,其修改操作(count++/count--)完全脱离了互斥锁的保护。具体来看:

  • 在func1和func2中,你先调用lk1.unlock()释放互斥锁,之后才修改count
  • 这意味着另一个线程可能在你修改count的同时,已经获取到互斥锁并读取count的旧值,直接导致count的状态逻辑混乱

为什么printf会"掩盖"问题?

printf是相对耗时的IO操作,当保留count++/count--后的printf时:

  • 当前线程解锁后会花时间执行printf,这段缓冲时间足够另一个线程完成唤醒、获取锁、检查条件的完整流程
  • 相当于人为给线程调度留出了同步窗口,暂时避免了竞态导致的死锁或循环卡住

而当你注释掉printf或者把它移到count++/count--上方时:

  • 线程解锁后会立刻修改count,此时另一个线程可能还未进入wait状态,或者已获取锁并读取了旧的count值
  • 最终导致某个线程的触发条件(count == 0或count == 1)永远无法满足,程序卡在第49次迭代(i=48)

修复方案:将count的修改纳入锁保护范围

正确的做法是确保count的所有读写操作都在互斥锁的保护下,同时使用wait的谓词版本避免虚假唤醒。修改后的代码示例:

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

using namespace std;
int count = 0;
mutex lock1;
condition_variable c1;
int resource;

void func1()
{
    for(int i = 0; i < 50; i++)
    {
        unique_lock <mutex> lk1(lock1);

        // 谓词版本wait:避免虚假唤醒,确保条件满足才继续执行
        c1.wait(lk1, []{ return count == 0; });

        printf("\nthread 1 is working on resource: %d and i: %d", resource, i);
        resource++;
        printf("\nthread 1 work completed, new value: %d and i: %d", resource, i);
        
        count++;
        c1.notify_one();
        // unique_lock会在作用域结束时自动释放锁,无需手动调用unlock
    }
}

void func2()
{
    for(int i = 0; i < 50; i++)
    {
        unique_lock <mutex> lk1(lock1);

        c1.wait(lk1, []{ return count == 1; });

        printf("\nthread 2 is working on resource: %d and i: %d", resource, i);
        resource--;
        printf("\nthread 2 work completed, new value: %d and i: %d", resource, i);
        
        count--;
        c1.notify_one();
    }
}

int main()
{
    auto start = chrono::high_resolution_clock::now();
    thread worker1(func1);
    thread worker2(func2);
    cout<<"\nthread started"<<endl;
    worker1.join();
    worker2.join();
    cout<<"\nthread completed"<<endl;
    auto end = chrono::high_resolution_clock::now();
    auto duration = chrono::duration_cast<chrono::milliseconds>(end - start);
    cout<<duration.count();
}

修复要点:

  1. 使用wait的谓词版本:即使收到唤醒信号,也会再次检查条件是否满足,彻底避免虚假唤醒
  2. count的修改在锁保护内:确保count的读写操作完全被互斥锁覆盖,消除竞态条件
  3. 依赖unique_lock自动解锁:避免手动调用unlock导致的时序错误,简化代码逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 10:13:22