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

在生产者/消费者模型中,如何验证原子对象与普通变量的差异?

如何观察普通int与std::atomic的线程同步差异

你的代码在默认编译环境(比如Debug模式)下看不到差异,是因为编译器未开启激进优化,且线程调度窗口太小,没触发普通int带来的未定义行为。要重现差异,需从以下几点修改:

1. 开启编译器优化

普通int的问题(循环优化、指令重排等)只有在Release/O2优化级别下才会显现。编译时给GCC/Clang添加-O2参数,或切换到VS的Release模式。

2. 放大线程调度窗口

给生产者线程的计算步骤添加延迟,让消费者线程有足够时间进入循环并读取未同步的变量:

#include <chrono>
#include <thread>

int calculate_val() {
    cout << "calculate_val " << endl;
    // 模拟耗时计算,拉长线程调度间隔
    this_thread::sleep_for(chrono::milliseconds(200));
    static int cnt = 0;
    return ++cnt;
}

3. 修改测试代码,增加变量不确定性

避免编译器将val优化为常量,同时通过多次运行增加触发异常的概率:
修改后的完整代码:

#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>

using namespace std;

// 切换这两行测试差异
// int a; 
atomic<int> a;
int val;

int calculate_val() {
    cout << "calculate_val " << endl;
    this_thread::sleep_for(chrono::milliseconds(200));
    static int cnt = 0;
    return ++cnt;
}

void process(int val) {
    cout << "process " << val << endl;
} 

void threadProducer() {
    val = calculate_val();
    a = 1;
}

void threadConsumer() {
    while (!a) {
        // 空循环,普通int下可能被优化为死循环
    }
    process(val);
}

int main() {
    // 多次运行增加触发异常的概率
    for (int i = 0; i < 10; ++i) {
        a = 0;
        val = 0;
        thread t2(threadConsumer);
        thread t1(threadProducer);
        t2.join();
        t1.join();
        cout << "--- 第" << i+1 << "次测试结束 ---" << endl;
    }
    return 0;
}

预期差异现象

当使用普通int a且开启O2编译后,大概率会出现以下情况之一:

  • 死循环:编译器将while (!a)优化为while (true),因为它认为当前线程不会修改a,程序永远卡在循环中。
  • 读取错误值:由于指令重排,编译器可能将a=1的指令提前到val=calculate_val()之前,导致消费者线程提前退出循环,读到未初始化的val(比如输出process 0)。
  • 缓存不一致:消费者线程一直读缓存中的旧值0,无法感知生产者线程对a的修改,陷入死循环。

而使用std::atomic<int> a时,所有测试都会正常输出calculate_val后紧跟process 1/2/...,不会出现死循环或错误值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 13:58:11