volatile 与诡异事件

来源:互联网 发布:java编程小游戏 编辑:程序博客网 时间:2024/04/25 12:47

  下面是我遇到的一个多线程程序中诡异事件的解决过程:主线程main启动 thread1 和 thread2,thread2保持阻塞,接到thread1发出的信号后工作,完毕后发出终了信号通知thread1,再次阻塞...直到thread1发出结束信号为止。代码如下:(信号用全局变量实现)
thread.h

 

thread.c



thread1.c


thread2.c

 

编译:
#gcc -o Thread thread.c thread1.c thread2.c -pthread
运行,正常结果是(指的是不会卡住,某些步骤会有先后变化)
#./Thread
thread1---thread1 start
thread1---send_task: 1
thread2---thread2 start
thread2---hanging and waiting...
thread2---got task: 1
thread2---Haahahhahahah!~
thread2---hanging and waiting...
thread1---send_task: 2
thread2---got task: 2
thread2---OhhAhhhOhhhhh!~
thread2---hanging and waiting...
thread1---send_task: 3
thread2---got task: 3
thread2---I'm dying...!~
thread2---thread2 over
thread1---thread1 over
Thread 1 returns: 0
Thread 2 returns: 0


但是结果多变,常常卡在


(thread2 运行越慢,越易重现,所以我故意在thread2.c 内加入 sleep(3);)
   程序逻辑没问题,不存在死锁,为什么会卡在上面两步?明明 task_ready 已由 thread2 修改过了,thread1的这个语句
为什么”不知道“?为什么一个线程察觉不到别的线程对公共全局变量的修改?这时候,忽然想到了一个从未用过的很奇特的限定符(qualifier)volatile:通知编译器,即使当前的程序(进程/线程)未修改它所标识的变量,此变量值也会改变。(可能其他程序/线程/硬件修改这个变量)。
为什么需要通知?先看看一句代码的汇编实现:(是用ARM实现的,我的机器是AMD64的,这两句没产生优化)


   因为,有时,编译器会对频繁使用的变量或者它认为“不会改变”的变量进行优化,对变量取值时不是从每次从内存-->寄存器;
而是直接一次取出保存在寄存器内,以后直接从寄存器取。问题就在于此:"似乎"不会改变的 task_ready和 main_task会被
thread2所修改,此处的“优化”就是问题所在,解决方式就是相关变量加上volatile即可。