首页 > 解决方案 > 原子之间的区别和整数

问题描述

我想知道两者之间是否有任何不同std::atomic<int>int如果我们只是在进行加载和存储。我不关心内存排序。例如考虑下面的代码

int x{1};

void f(int myid) {

    while(1){
        while(x!= myid){}
        //cout<<"thread : "<< myid<<"\n";
        //this_thread::sleep_for(std::chrono::duration(3s));
        x = (x % 3) + 1;
    }
}

int main(){

    thread x[3];
    for(int i=0;i<3;i++){

        x[i] = thread(f,i+1);
    }

    for(int i=0;i<3;i++){

        x[i].join();
    }
}

现在输出(如果您取消注释 cout)将是

主题:1

主题:2

主题:3

...

我想知道更改int xto是否有任何好处atomic<int> x

标签: c++c++11stdatomic

解决方案


考虑您的代码:

void f(int myid) {
    while(1){
        while(x!= myid){}
        //cout<<"thread : "<< myid<<"\n";
        //this_thread::sleep_for(std::chrono::duration(3s));
        x = (x % 3) + 1;
    }
}

如果程序没有未定义的行为,那么您可以期望在f调用x时至少会从堆栈中读取一次,但是这样做后,编译器没有理由认为任何更改x都会发生在函数之外,或者在函数返回之前,函数内所做的任何更改都x需要在函数外可见,因此它有权读取xCPU 寄存器,继续查看相同的寄存器值并将其与myid- 这意味着它将要么立即通过,要么永远卡住。

x != myid然后,允许编译器假设他们会取得进展(参见 C++ 标准中的 Forward Progress),因此他们可以得出结论,因为如果,x不可能等于,他们将永远不会取得进展myid,并删除内部while循环。while (1) x = (x % 3) + 1;同样,简化为可能是寄存器的外部循环x- 没有取得进展,也可以被消除。或者,编译器可以退出循环,但删除对x.

将您的代码放入在线 Godbolt 编译器资源管理器并在-O3优化时使用 GCC 主干进行编译,f(int) 代码为:

f(int):
.L2:
    jmp     .L2

如果您使x原子成为原子,那么编译器在访问/修改它时不能简单地使用寄存器,并假设在函数返回之前会有一个很好的时间来更新它。它实际上必须修改内存中的变量并传播该更改,以便其他线程可以读取更新的值。


推荐阅读