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

inkz8wg9  于 2023-03-05  发布在  其他
关注(0)|答案(1)|浏览(79)

我想实现以下内容
1.两个线程:producer(可更改共享变量a)、consumer(等待共享变量更改)
1.我想使用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;
}
envsm3lx

envsm3lx1#

你的场景太简单了。在一个具有强有序内存语义的体系结构上(例如x86及其后代,如果你提到“我的PC”,这可能就是你正在使用的),编译器优化关闭,只要编译器不在函数调用之前重新排序threadProducera = 1;(它没有理由这样做),不缓存while (!a)测试的a值,不消除while(a){}循环,原子不会有什么不同。
如果没有原子,您现有的代码可能会失败:
1.在具有弱序存储语义的机器上(对aval的写入可以以任何顺序在threadConsumer中变为可见,并且它们变为可见可能需要任意长的时间)我怀疑iostream库的内置锁定以及它 Package 的Cx 1 m9n1x可能会触发缓存刷新,因此,您试图“观察”争用的I/O实际上是在“改变"行为。
1.在任何机器上,如果编译器选择将a的值缓存在threadConsumer中以提高效率(可以缓存非volatile、非atomic值以保存开销,因为假设它们仅从一个线程访问,并且加载线程不会更改它)。
1.在任何机器上,如果编译器decides to eliminates the while (a) {} loop,由于非原子a可能会创建一个无限循环,如果按照#2中的建议缓存。
1.在任何一台机器上,如果编译器选择将a = 1;排在val = calculate_val();之前(因为两行都不依赖于另一行计算的值,所以优化编译器可以选择以任何一种顺序进行),在实践中,给定前面提到的I/O层的锁定,编译器 * 不会 * 交换这些行,但如果I/O被删除,它 * 可以 * 交换。
我不会给予一个可能出错的例子,因为粗略的搜索会找到各种各样的例子,说明何时以及为什么使用原子。重要的是要知道,即使在你简单的场景中,你认为没有原子是安全的,但事实并非如此。即使打开优化也可能导致你的代码使用非原子a,在x86-64机器上停止运行。并且x86-64已经是简单模式上的线程/原子。

相关问题