为何GCC对多线程和单线程场景下的全局变量done优化结果不同
GCC -O2优化下全局变量在多/单线程场景的优化差异原因
核心原因是C语言标准定义的编译器优化默认遵循单线程执行语义,不会默认假设全局变量会被当前线程以外的逻辑异步修改,两个场景的具体优化逻辑差异如下:
单线程场景的优化逻辑
- 编译器做O2级别的过程间优化时,可以清晰追踪到单线程执行流里的所有变量修改操作:
main函数先调用func(),func()内部明确对全局变量done做了赋值为true的操作,且func()调用在while循环之前。 - 编译器可以确定进入while循环前
done已经是true,因此不会把done的读取优化成寄存器缓存的固定值,甚至可以直接把空的while循环整个优化删除,程序自然可以正常执行到结束。
多线程场景的优化逻辑
- GCC 5.4版本默认不会将
pthread_create这类线程库调用识别为「变量可能被跨线程修改」的边界,C语言标准也没有要求编译器默认感知多线程下的异步变量修改。 - 编译器在优化
main函数的执行流时,只会检查当前main线程里对done的修改操作:main线程里除了初始化done为false外,没有任何其他修改done的逻辑,因此会直接将while循环的判断条件!done优化为永真的常量,让循环变成无限空循环,也就不会每次循环都重新从内存读取done的最新值。
补充说明
很多人会用volatile解决这个问题,但是volatile只能保证编译器不会省略变量的内存读写操作,无法保证多线程下的内存可见性和操作顺序,更规范的做法是用C11标准的_Atomic原子类型修饰done,或者用互斥锁保护done的所有读写操作,这类同步原语都会明确告诉编译器变量存在异步修改的可能,不会做过度优化。
内容的提问来源于stack exchange,提问作者Milan
相关产品推荐
相关产品推荐

