多线程和指令序列

Jou*_*uny 9 c++ multithreading

在学习多线程编程时,我编写了以下代码。

#include <thread>
#include <iostream>
#include <cassert>

void check() {
    int a = 0;
    int b = 0;
    {
        std::jthread t2([&](){
            int i = 0;
            while (a >= b) {
                ++i;
            }
            std::cout << "failed at iteration " << i << "\n"
                      // I know at this point a and b may have changed
                      << a << " >= " << b << "\n";
            std::exit(0);
        });
        std::jthread t1([&](){
            while (true) {
                ++a;
                ++b;
            }
        });
    }
}

int main() {
    check();
}
Run Code Online (Sandbox Code Playgroud)

因为++a总是发生在++ba 之前,所以应该总是大于或等于 b。但实验表明,有时b > a。为什么?是什么原因造成的?我该如何执行?

即使当我替换int a = 0;int a = 1000;这也会让这一切变得更加疯狂。

程序很快退出,因此不会发生 int 溢出。我发现汇编中没有指令重新排序可能导致此问题。

Sam*_*hik 8

由于 ++a 总是发生在 ++ba 之前,所以 ++ba 应该总是大于或等于 b

仅在其执行线程中。并且仅当执行线程可以观察到这一点时。

C++ 需要某种显式“同步”,以便一个执行线程所做的更改对其他执行线程可见。

     ++a;
     ++b;
Run Code Online (Sandbox Code Playgroud)

仅凭这些语句,该执行线程实际上无法“区分”是否首先递增ab。因此,C++ 允许编译器实现它想要的任何优化或代码重新排序步骤,只要它在执行线程中没有可观察到的影响b,并且如果实际生成的代码首先递增,则不会有任何可观察到的影响。这个执行线程不可能区分出差异。

但是,如果有一些“查看”的中间语句a,那么这将不再成立,并且编译器将需要a在以某种方式使用它之前实际生成递增的代码。

这只是这个执行线程。即使可以观察到此执行线程a中更改的相对b顺序,标准也允许 C++ 编译器以任何顺序实际递增实际变量,只要还有任何其他调整可以实现这一点不可观察到的。但它可以被另一个执行线程观察到。为了防止这种情况,有必要采取显式同步步骤,使用互斥体、条件变量和 C++ 执行线程模型的其他部分。