如何检查生产者/消费者模式中的原子对象影响?

问题描述 投票:0回答:1

我想实现以下内容

  1. 两个线程:
    producer
    (可以改变共享变量
    a
    ),
    consumer
    (等待共享变量改变)
  2. 我想用atomic来同步线程

问题 - 我用

int a;
std::atomic<int> a;
运行我的代码,但在 100% 的情况下,我在我的 PC 上得到相同的结果。

calculate_val 
process 1

如何查看有和没有原子的代码变化?

using namespace std;

atomic<int> a; // I was replacing with int a; and always have the same result
int val;

int calculate_val() {
    cout << "calculate_val " << endl;
    return 1;
}

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

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

void threadConsumer() {
    while (!a) {
        
    }
    process(val);
}




int main() {
    thread t2(threadConsumer);
    thread t1(threadProducer);

    t2.join(); // was trying to change order
    t1.join();
    return 0;
}
c++ multithreading atomic
1个回答
1
投票

你的场景太简单了。在具有强有序内存语义的架构上(例如 x86 和后代,如果你提到“我的电脑”,它可能就是你正在使用的),关闭编译器优化,只要编译器不重新排序

 threadProducer
a = 1;
在函数调用之前(它没有理由这样做),不会为
a
测试缓存
while (!a)
的值,并且不会消除
while(a){}
循环,原子不会有什么不同。

如果没有原子,您现有的代码可能会失败:

  1. 在具有弱有序内存语义的机器上(对
    a
    val
    的写入可以在
    threadConsumer
    中以任何顺序可见,并且它们可能需要任意长的时间才能变得可见)。我怀疑
    iostream
    库的内置锁定,以及它包装的 C
    stdio
    可能会触发缓存刷新,所以你的 I/O 试图 observe 比赛实际上是 changing 行为.
  2. 在任何机器上,如果编译器选择在
    a
    中缓存
    threadConsumer
    的值以提高效率(非
    volatile
    ,非
    atomic
    值可以被缓存以节省开销,因为假设它们只能从访问一个线程,加载线程没有改变它)。
  3. 在任何机器上,如果编译器 决定消除
    while (a) {}
    循环
    ,因为非原子
    a
    如果按照 #2 中的建议进行缓存,则可能会创建一个无限循环。
  4. 在任何机器上,如果编译器选择在
    a = 1;
    之前重新排序
    val = calculate_val();
    (因为这两行都不依赖于另一行计算的任何值,优化编译器可以选择以任一顺序执行它们)。实际上,鉴于上述 I/O 层中的锁定,编译器 不会 交换这些行,但如果 I/O 被删除,它 可以

我不打算给出可能出错的例子,因为粗略的搜索会找到各种关于何时以及为什么使用原子的例子。重要的是要知道,即使在您 thought 没有它们就安全的简单场景中,事实并非如此。即使开启优化也可能导致您的非原子代码

a
停止在 x86-64 机器上运行,而 x86-64 已经在简单模式下进行线程/原子操作。

© www.soinside.com 2019 - 2024. All rights reserved.