在“C++ 中的实时自白”演讲的 6:44 ,演讲者展示了有缺陷的代码,本质上可以归结为:
x
x
(循环)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。
我做错了什么?如何正确创建此并发错误的最小示例?
我永远不会认为我会写这样的东西,但有一个“固定”的例子,很好“正确地打破”。至少在我的盒子上。
#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
如您所见,“已更改”消息永远不会出现,因为编译器已将其优化掉。
与您的代码相反,我避免了对编译器看不到的函数的所有调用。如果你调用“未知”的东西,编译器通常会假设全局变量可能会受到影响 - 它并不真正关心你是否正在调用一个标准化的库函数,这将允许他假设全局变量没有改变。