最近工作中同事在研究 volatile 关键字的时候,遇到了个有趣的情况,和 g++ 的编译优化有关,在这里记录一下~
# 1. 遇到问题
这次遇到的问题是这样的,我们使用这样一段代码测试 volatile 的特性:
#include <pthread.h> | |
#include <stdio.h> | |
#ifdef _VOLATILE | |
volatile bool b = true; | |
#else | |
bool b = true; | |
#endif | |
void* test(void* p) | |
{ | |
b = false; | |
return 0; | |
} | |
int main(){ | |
pthread_t thread_id; | |
pthread_create(&thread_id, NULL, &test, NULL); | |
while (b) | |
{ | |
} | |
return 0; | |
} |
尝试了该段代码在变量 b 是否声明 volatile,是否启用 O2 编译优化的几种情况下的执行结果。
结果发现,在未声明 volatile、启用 O2 的情况下,程序卡死了,但其他情况均可以正常退出。
# 2. 原因分析
没想明白为啥,于是查看了汇编代码,发现问题在于对 while (b) 这行代码的优化。
非 volatile,无 O2 的 while (b):
...
LBB1_1: ## =>This Inner Loop Header: Depth=1
testb $1, _b(%rip)
je LBB1_3
## %bb.2: ## in Loop: Header=BB1_1 Depth=1
jmp LBB1_1
LBB1_3:
...
非 volatile,开启 O2 优化的 while (b):
...
cmpb $0, _b(%rip)
je LBB1_2
.p2align 4, 0x90
LBB1_1: ## =>This Inner Loop Header: Depth=1
jmp LBB1_1
LBB1_2:
...
可以看到非 O2 的情况下,b 的判断是在每次循环都会判断的,而 O2 的优化中,由于 b 只在子线程中修改,所以编译器判定 b 不会变化,仅比较了一次,然后开始死循环了。
而 b 声明为 volatile 以后,编译器在优化时不会把判断的流程优化掉,因此不会卡死。
# 3. 总结
经过这次主要得出的结论:
1.O2 优化下,会将主线程中不会变化的值优化,当做类似常量来处理;
2.volatile 在多线程下可以防止这种优化带来的问题。