原子<int>和int之间的区别

Gtr*_*rex 0 c++ c++11 stdatomic

我想知道我们是否只是进行加载和存储之间是否有任何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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

线程:1

螺纹数:2

螺纹数:3

...

int x我想知道更改为有什么好处吗atomic<int> x

Ton*_*roy 5

考虑你的代码:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

然后,编译器可以假设它们会取得进展(请参阅 C++ 标准中的前进进展),因此它们可以得出结论,因为如果x != myid,x不可能等于,它们永远不会取得进展myid,并删除内部while循环。类似地,简化为寄存器的外循环while (1) x = (x % 3) + 1;不会x取得进展,也可以被消除。或者,编译器可以离开循环,但删除 上看似毫无意义的操作x

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

f(int):
.L2:
    jmp     .L2
Run Code Online (Sandbox Code Playgroud)

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

  • [关于前进进度的标准(我能找到的最新草案)](http://eel.is/c++draft/intro.progress) (2认同)
  • @TonyDelroy:是的,在 GNU C/C++ 中, `asm("" ::: "memory")` 是一个编译器障碍,强制实际内存内容与抽象机同步。(除了可以证明没有其他线程可以引用的变量之外,例如其地址未逃脱此函数的局部变量......) (2认同)