由于一个线程在线程启动时加载值而导致的 C++ 并发错误的最小示例

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

在“C++ 中的实时自白”演讲的 6:44 ,演讲者展示了有缺陷的代码,本质上可以归结为:

  1. 线程 A 写入一个名为
    x
  2. 的非同步整数
  3. 很久以后,线程 B 读取
    x
    (循环)
  4. 线程 B 看不到
    x
    的更新值,因为优化编译器意识到线程 B 从未更改过
    x
    ,因此
    x
    的值可以在线程启动时加载一次,再也不会加载。

演讲者试图证明即使在单核机器上同步也是必要的,否则编译器优化可能会导致代码出现错误。

我正在尝试给出这个错误的最小示例。这是我所拥有的:

#include <string>
#include <iostream>
#include <thread>
#include <unistd.h>

int var;  // unsynchronized, i.e., no std::atomic or anything

void f() {
    for (int i = 0; i < 10; i++) {
        sleep(1);
        int x = var * var;
        std::cout << x << std::endl;
    }
}

int main() {
    var = 1;

    std::thread t(f);
  
    sleep(2);
    var = 10;

    t.join();
}

我的希望是,在

f()
内部,编译器会意识到它可以在线程开始时加载
var
并计算
x
一次,而不是在循环中一遍又一遍地加载
var
,从而永远不会看到主线程上
var
的值从 1 更改为 10。

不幸的是,程序的行为“正确”,如下所示:

$ clang++ -O3 test.cpp && ./a.out
1
100
100
100
...

我希望输出都是

1
s。

我做错了什么?如何正确创建此并发错误的最小示例?

c++ multithreading concurrency atomic
1个回答
0
投票

我永远不会认为我会写这样的东西,但有一个“固定”的例子,很好“正确地打破”。至少在我的盒子上。

#include <iostream>
#include <thread>
#include <unistd.h>

bool test=false;  // unsynchronized, i.e., no std::atomic or anything
volatile bool quit=false;

void f() {
    std::cout << "thread: " << test << "\n";
    while (!quit) {
        if (test) {
            std::cout << "changed\n";
            break;
        }
    }
    std::cout << "thread: " << test << "\n";
}

int main() {
    std::thread t(f);

    sleep(1);
    test=true;
    std::cout << "main: " << test << "\n";

    sleep(1);
    quit=true;

    t.join();
}
stieber@gatekeeper:~ $ g++ -O3 Test.cpp; ./a.out
thread: 0
main: 1
thread: 1

如您所见,“已更改”消息永远不会出现,因为编译器已将其优化掉。

与您的代码相反,我避免了对编译器看不到的函数的所有调用。如果你调用“未知”的东西,编译器通常会假设全局变量可能会受到影响 - 它并不真正关心你是否正在调用一个标准化的库函数,这将允许他假设全局变量没有改变。

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