最近工作中同事在研究 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 在多线程下可以防止这种优化带来的问题。

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Nirvana 支付宝

支付宝