我迷路了..我想使用编译器资源管理器来试验多线程 C 代码,并从一段简单的代码开始。该代码是用-O3.
static int hang = 0;
void set_hang(int x) {
hang = x;
}
void wait() {
while (hang != 0) {
// wait...
}
}
Run Code Online (Sandbox Code Playgroud)
令我惊讶的是,这是编译器的输出:
set_hang(int):
mov dword ptr [rip + hang], edi
ret
wait():
ret
Run Code Online (Sandbox Code Playgroud)
我花了一段时间才注意到我正在将代码编译为 C++ 而不是 C。切换到 C 给了我一些我所期望的东西:
set_hang:
mov DWORD PTR hang[rip], edi
ret
wait:
mov eax, DWORD PTR hang[rip]
test eax, eax
je .L3
.L5:
jmp .L5
.L3:
ret
Run Code Online (Sandbox Code Playgroud)
因此,当编译为 C++ 时,wait()无论之前将哪个值传递给 ,总是返回set_hang()。我通过在我的电脑上编译代码来确认这一点。该代码立即存在,而我希望它永远挂起:
int main(void) {
set_hang(1);
wait();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
事实上,如果我使用gcc而不是with 来编译它g++,它就会挂起。
我尝试了不同的编译器(Clang 和 GCC),这只发生在 Clang 12.0.0 或 higer 或 GCC 10.1 或更高版本上。如果我--std=c++98也通过了我期望的代码,那么它似乎是 C++11 及更高版本特有的代码。
static从 中删除关键字hang不会影响发出的程序集。
这里发生了什么?我写 C++ 已经有几个月了,所以我可能会错过一些关于最新、最伟大的外来 C++ 黑魔法的知识,但这确实是简单的代码。我一无所知。
编辑:即使这个程序也被完全优化掉了:
// test.cpp
static int hang = 0;
static void set_hang(int x) {
hang = x;
}
static void wait() {
while (hang != 0) {
// wait...
}
}
int main(void) {
set_hang(1);
wait();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
编译器输出:
main:
xor eax, eax
ret
Run Code Online (Sandbox Code Playgroud)
对于 Ubuntu 上的 GCC 版本 10.3.0:
该命令将挂起:g++ -O1 -o test test.cpp && ./test
这个命令不会:g++ -O2 -o test test.cpp && ./test
这是因为以下规则:
[介绍.进展]
该实现可能假设任何线程最终都会执行以下操作之一:
- 终止,
- 调用库 I/O 函数,
- 通过易失性左值执行访问,或者
- 执行同步操作或原子操作。
编译器能够证明进入循环的程序永远不会执行任何列出的操作,因此可以假设永远不会进入循环。