多线程场景下应在哪里设置memory barrier确保循环观测到变量变更?
内存屏障的放置位置
你需要在循环体内部、每次循环迭代的末尾插入编译器内存屏障,示例代码如下:
while (!stop) { // do something asm volatile("" ::: "memory"); // GCC/Clang 下的编译器内存屏障 }
这个屏障的作用是告诉编译器:不得将内存访问操作跨该屏障重排,也不能将读取到的stop变量值缓存到寄存器中跨屏障复用。这样编译器就必须每次循环都重新从内存读取stop的值,不会把代码优化为你提到的单次判断后死循环的形式。
如果是多核心运行的场景,你还需要配套加CPU层面的内存屏障:
- 写线程修改
stop为true之后,插入写屏障(如x86下的sfence),保证修改被刷到CPU缓存共享域 - 读线程在读取
stop之前插入读屏障(如x86下的lfence),保证读取到最新的缓存值
屏障是否能保证修改可见性
仅加编译器屏障只能保证单核心抢占式调度场景下的可见性,多核心场景下不足以保证:
编译器屏障只会阻止编译器的指令重排和变量缓存优化,不会处理CPU层面的缓存不一致问题。修改stop的线程的修改可能暂时存在对应核心的store buffer中,没有同步到缓存一致性协议的共享域,读线程所在核心的缓存行还保留旧值,就算每次从内存读取也拿不到最新值。
要保证跨核心的可见性,必须搭配CPU层面的读写屏障,或者更简单的方案是直接将stop定义为原子变量(如C++的std::atomic<bool>、Java的volatile boolean),语言标准的原子变量实现会自动处理编译器重排、CPU屏障、缓存同步的所有问题,不需要手动插入屏障。
内容的提问来源于stack exchange,提问作者user6412004
相关产品推荐
相关产品推荐

