在生产者/消费者模型中,如何验证原子对象与普通变量的差异?
如何观察普通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
相关产品推荐
相关产品推荐

