分享一个代码优化导致的死循环

1 月 4 日
 echoechoin

现象: gdb bt 卡在 spinlock_unlock() 位置。

  1. 查了半天代码,我的每一个锁都没有问题。给我看吐血了。

  2. 最后没辙了灵机一动 gdb disassemble 发现这里死循环了:

原来是一个标志位没有设置为 volatile ,代码被优化了。大概的代码如下:

while (true) {
    // 多线程中可以被修改的一个标志
    if (flags_1 == STATUS_1) { 
        continue
    }
    // 其他逻辑
    // ... ...
}

但是为什么不是必现的 BUG 呢? AI 给了一堆解释,但是没怎么看懂。

6726 次点击
所在节点    C
31 条回复
anytk
1 月 4 日
跨线程的话,一般推荐用原子变量来做标志,内存序可以用 relaxed.

volatile 基本留给硬件寄存器读写和 sig_atomic_t 了.
echoechoin
1 月 4 日
@anytk 想来也是不应该写这种死循环,AI 建议使用 refcnt 可以多个线程在 STATUS_1 状态下继续后续的代码,但是我也懒得改了。。。
aviator
1 月 4 日
volatile 提供了多线程访问的可见性,是得加
qzhai
1 月 4 日
分享一个死循环给我?我写死循环的经验可是多得很
PTLin
1 月 4 日
简单来说就是你没用任何同步手段,例如原子变量,内存屏障,volatile 。你这种情况下有可能会遇到
指令重排,例如把你这个判断的代码重排到后面。
从缓存中读取,例如把你这个 flags 放到寄存器里,然后从寄存器里读出。
操作拆分,例如把你这个读取操作在汇编的实现中拆分成两次内存操作。

相关的东西你还是去了解下内存序什么的吧。
wfg
1 月 4 日
你贴的代码,不管有没有 continue 不都是继续循环吗?
ccpp132
1 月 4 日
c++的话 volatile 也不算对,应该用 atomic ,这点和 java 还不是一回事
PTLin
1 月 4 日
@PTLin 甚至在一些情况下,编译器发现你后续代码没用操作这个变量,虽然你这个变量的操作是在其他线程,但是编译器并不知道这些信息,因为你没用同步手段,编译器可能会激进的吧你这个判断删除。
yyysuo
1 月 4 日
@qzhai 大家都不怎么服气的。
mmdsun
1 月 4 日
C 语言多线程经典 BUG 了。 让 ai 写一个 c 语言多线程自旋代码吧。不用原子锁条件变量肯定爆炸
w568w
1 月 4 日
chenyu0x00
1 月 4 日
这个不是多线程的 bug ,这个是 C/C++编译器的"特性"。
相当于有这样一段代码:
```c++
int flag;

while (flag == 0) {
}
```
编译器在不进行优化(-O0)的情况下,每次 while 循环都会从内存中读 flag 的值,并和 0 进行判断。
当使用了编译器优化的情况下,编译器就会发现"这个程序员真 SB ,为什么每次都要从内存中读 flag 的值呢,我直接读一次,放到寄存器里面,之后访问速度就更快了,判断也可以优化成一次",于是代码就变成这样了:
```c++
register eax = flag;
if (eax == 0) {
while (true) {
}
}
```
你会发现这个优化在单线程的情况下是完全没有问题的,在多线程情况下就出问题了。
而使用 volatile int flag 就是告诉编译器"不要偷懒,我让你从内存读 flag 你就从内存读,让你写到内存就写到内存"。

不过如上面所说,建议多线程通信使用 atomic ,因为 atomic 还解决了多线程情况下的内存序的问题。
opengps
1 月 4 日
@wfg #6 我猜是类似于 plc 的设备检测逻辑,真正的逻辑在后面一段
litchinn
1 月 4 日
为什么要加 volatile 很好理解,cpu 有多级缓存,volatile 强制从内存中读取最近修改
但是要问为什么不是必现的 BUG 这就太复杂了
JIT 优化,代码是否触发了上下文切换,缓存行是否失效了触发读取主存,两个线程的 CPU 是否共享同一个缓存都可能影响
litchinn
1 月 4 日
@litchinn 看错节点了,没有 JIT
cnbatch
1 月 4 日
多线程读写的话,能用 atomic 就尽量用,C 语言也有内置的(记得是从 C11 开始的)
midraos
1 月 4 日
@ccpp132 #7 c/c++中 volatile 无法保证多线程下的可见性,应该是有 atomic 或使用内存屏障
aminobody
1 月 4 日
yxd19
1 月 4 日
我之前卡在 cuda device code 的内存模型上很久。。
araraloren
1 月 4 日
有原子变量,为何不用?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://study.congcong.us/t/1182913

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX