下面是我遇到的一个多线程程序中诡异事件的解决过程:主线程main启动 thread1 和 thread2,thread2保持阻塞,接到thread1发出的信号后工作,完毕后发出终了信号通知thread1,再次阻塞...直到thread1发出结束信号为止。代码如下:(信号用全局变量实现)
thread.h
#include <pthread.h> #include <stdio.h> #ifndef ARG extern pthread_mutex_t mutex; extern pthread_cond_t cond; extern int task_ready; extern int main_task; //extern volatile int task_ready; //extern volatile int main_task; enum Task { IDLE = 0, SMILE, MOAN, DIE }; #else pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; int task_ready; int main_task; //volatile int task_ready; //volatile int main_task; #endif void func_1(); void func_2();

thread.c

#define ARG #include "thread.h" int main() { pthread_t thread1, thread2; int iret1, iret2; iret1 = pthread_create( &thread1, NULL, (void*)func_1, NULL); iret2 = pthread_create( &thread2, NULL, (void*)func_2, NULL); pthread_join( thread1, NULL); pthread_join( thread2, NULL); printf("Thread 1 returns: %d/n",iret1); printf("Thread 2 returns: %d/n",iret2); return (0); }

thread1.c

#include "thread.h" void send_task(int task) { printf("thread1---send_task: %d /n",task); while(!task_ready) { //printf("thread1---waiting for task to be ready/n"); } pthread_mutex_lock(&mutex); task_ready = 0; main_task = task; pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); while(main_task) { //printf("thread1---waiting for task to be done/n"); } } void func_1() { printf("thread1---thread1 start /n"); send_task(SMILE); send_task(MOAN); send_task(DIE); printf("thread1---thread1 over /n"); }

thread2.c

#include "thread.h" void func_2() { int over = 0; printf("thread2---thread2 start /n"); while(!over) { pthread_mutex_lock(&mutex); task_ready = 1; printf("thread2---hanging and waiting.../n"); pthread_cond_wait(&cond,&mutex); printf("thread2---got task: %d /n",main_task); switch(main_task) { case SMILE: printf("thread2---Haahahhahahah!~/n"); break; case MOAN: printf("thread2---OhhAhhhOhhhhh!~/n"); break; case DIE: default: printf("thread2---I'm dying...!~/n"); over = 1; break; } sleep(3); main_task = IDLE; pthread_mutex_unlock(&mutex); } printf("thread2---thread2 over /n"); }

编译:
#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

但是结果多变,常常卡在
while(!task_ready) { //printf("thread1---waiting for task to be ready/n"); } //或者: while(main_task) { //printf("thread1---waiting for task to be done/n"); }

(thread2 运行越慢,越易重现,所以我故意在thread2.c 内加入 sleep(3);)
   程序逻辑没问题,不存在死锁,为什么会卡在上面两步?明明 task_ready 已由 thread2 修改过了,thread1的这个语句
为什么”不知道“?为什么一个线程察觉不到别的线程对公共全局变量的修改?这时候,忽然想到了一个从未用过的很奇特的限定符(qualifier)volatile:通知编译器,即使当前的程序(进程/线程)未修改它所标识的变量,此变量值也会改变。(可能其他程序/线程/硬件修改这个变量)。
为什么需要通知?先看看一句代码的汇编实现:(是用ARM实现的,我的机器是AMD64的,这两句没产生优化)
#... #int a=0;while(a); #... .LFB0: .L2: b .L2 #编译器认为永远 a = 0;故直接优化成死循环。 #另一个 #volatile int a=0;while(a); .LFB0: ldr r2, .L6 .pad #8 sub sp, sp, #8 .LCFI0: mov r3, #0 str r3, [sp, #4] .L2: ldr r3, [sp, #4] cmp r3, r2 ble .L2 add sp, sp, #8 @ sp needed for prologue bx lr

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

volatile 与诡异事件相关推荐

  1. z-index诡异事件之背锅侠

    第一次在这上面发文哈,平时都只是在这里学(fu)习(zhi),准备记录一下我踩过和正在踩的坑吧. 今天找了半天小程序选择框的组件,奈何并没有找到官方的组件,就准备自己写(fu zhi)一个.结果写遮罩 ...

  2. 一个线程罢工的诡异事件

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 背景 事情(事故)是这样的,突然收到报警,线上某个应用里业务逻辑没 ...

  3. 生产问题:一个线程罢工的诡异事件

    事情(事故)是这样的,突然收到报警,线上某个应用里业务逻辑没有执行,导致的结果是数据库里的某些数据没有更新. 虽然是前人写的代码,但作为 Bugmaker&killer 只能咬着牙上了. 因为 ...

  4. 数据结构与索引-- mySql索引诡异事件

    什么时候使用B+树索引 并不是所有查询条件下出现的列都需要添加索引.对于什么时候添加索引,我们通过经验判断,访问表中很少一部分行时候,使用B+树索引才有意义. 对于性别字段,地区字段,类型字段,他们取 ...

  5. (诡异事件)iframe标签后面的alert不执行

    今天做项目的过程中,发现一个非常奇怪的事情.iframe标签后面的js 不执行? 把代码贴出来,看看有没有大牛碰到这种情况.谢谢. <html xmlns="http://www.w3 ...

  6. c - 数据结构实验之图论三:判断可达性_IT世界的诡异事件,2020为何算法和数据结构面试题会如此火爆?...

    只有熟练掌握基础的数据结构与算法,才能对复杂问题迎刃有余. 很多时候,你即使提前复习了这些最常见的面试算法题,你依旧无法通过算法面试!为什么? 你在提前准备复习的时候,在网上找了半天相应题目的分析文章 ...

  7. svn合并时候的诡异事件

    使用subclipse 创建合并主干的时候,发现总是无法在下面的界面直接选择主干. 然后就选择直接输入相对路径(因为以前有一个分支就是这样做的),但是subclipse却会直接把你 在前面加上当前分支 ...

  8. 记一次哭笑不得的诡异事件

    今天有个开发跑过来质疑我之前给他们灌输的数据库中尽量避免隐式转换的概念,理由是遇到一个where条件中不做隐式转化的SQL查询不到结果. 表结构如下: mysql> desc m; +----- ...

  9. C# 类型实例化的语法糖--unity下诡异结果

    类型实例化语法糖就是如下的用法: public class Abc {public int ID { get; set; }public string Name { get; set; }public ...

  10. CSS进阶(4)—— 温和padding中的诡异CSS现象

    盒模型的四大家族,原以为content部分比较复杂,单独写了一章,但在看padding部分的时候又遇到一个非常诡异的CSS现象,经过不断测试,终于得到一个比较接近于"真相"的解释, ...

最新文章

  1. 微软小冰作词又作曲,网友:要出道的节奏吗?
  2. poj1050 To the Max
  3. 使用LSTM进行情感分析
  4. 合并文件夹中子目录_如何整理文件夹,看这一篇就够了,简单易学
  5. 不能以根用户身份运行 Google Chrome 浏览器
  6. win7鼠标指针主题包_Windows10系统用键盘来代替鼠标操作的方法
  7. android15版本号手机,Lineage OS 15.1正式发布:基于Android 8.1
  8. HDU2014 青年歌手大奖赛_评委会打分【入门】
  9. python计算空类型_python的变量 类型和存储
  10. Android CountDownTimer示例
  11. mysql表空间预估_MYSQL实战优化——数据页、表空间
  12. oracle取消正在回滚的事务,Oracle11新特性——撤销事务(一)
  13. 360急速浏览器有道词典屏幕取词问题
  14. WIN10 无法保存游戏配置文件 每次进游戏都需要重新设置的问题解决方案
  15. 万用表测电容方法-电子技术方案
  16. 计算机可能发科学,行测真题_随着科学技术的进步,电脑的问世让人类制造这样的机器“耳朵”成为可能...
  17. 【计算机组成与结构】中央处理器
  18. 和李兄之《定风波· 冬峦轻寒桂落香》一首
  19. EasyExcel第一次导出文件耗时长的问题
  20. edittext 软键盘上方_Android 软键盘的全面解析,让你不再怕控件被遮盖

热门文章

  1. 我,马斯克:地球首富却要葬在火星
  2. Serial Scope——基于QCustomPlot的串口虚拟示波器开发过程
  3. DRAM发展年历——电容方向
  4. 华为U8818 刷入Recovery教程+刷机
  5. 畅游或将私有化退市股价涨近50%,搜狐“吃饱”后能重回巅峰吗?
  6. 手提电脑无法管理计算机,细说笔记本键盘无法输入怎么办
  7. Linux 程序性能分析与优化
  8. oracle 产品宣传片,史上最牛宣传片!河南的美已惊艳了世界!
  9. android logo颜色渐变,华为悄然更新品牌Logo:无渐变色,更加扁平化
  10. 加拿大计算机工程研究生,加拿大维多利亚大学电子与计算机工程系副教授诚招硕士研究生 - 导师招生 - 小木虫 - 学术 科研 互动社区...