微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > 第60节:用关中断和互斥量来保护多线程共享的全局变量

第60节:用关中断和互斥量来保护多线程共享的全局变量

时间:11-22 来源:互联网 点击:
开场白:

在前面一些章节中,我提到为了防止中断函数把某些共享数据破坏,在主函数中更改某个数据变量时,应该先关闭中断,修改完后再打开中断;我也提到了网友“红金龙吸味”关于原子锁的建议。经过这段时间的思考和总结,我发现不管是关中断开中断,还是原子锁,其实本质上都是程序在多进程中临界点的数据处理,原子锁有个专用名词叫互斥量,而我引以为豪的状态机程序框架,主函数的switch语句,外加一个定时中断,本质上就是2个独立进程在不断切换并行运行。

为什么要保护多线程共享全局变量?因为,多个线程同时访问同一个全局变量,如果都是读取操作,则不会出现问题。如果一个线程负责改变此变量的值,而其他线程负责同时读取变量内容,则不能保证读取到的数据是经过写线程修改后的。

这一节要教大家一个知识点:如何用关中断和互斥量来保护多线程共享的全局变量。

具体内容,请看源代码讲解。

(1)硬件平台:

基于朱兆祺51单片机学习板

(2)实现功能:

在第5节的基础上略作修改,让蜂鸣器在前面3秒发生一次短叫报警,在后面6秒发生一次长叫报警,如此反复循环。

(3)源代码讲解如下:

#include "REG52.H"

#define const_time_3s 1332 //3秒钟的时间需要的定时中断次数

#define const_time_6s 2664 //6秒钟的时间需要的定时中断次数

#define const_voice_short 40 //蜂鸣器短叫的持续时间

#define const_voice_long 200 //蜂鸣器长叫的持续时间

void initial_myself();

void initial_peripheral();

void delay_long(unsigned int uiDelaylong);

void led_flicker();

void alarm_run();

void T0_time(); //定时中断函数

sbit beep_dr=P2^7; //蜂鸣器的驱动IO口

unsigned char ucAlarmStep=0; //报警的步骤变量

unsigned int uiTimeAlarmCnt=0; //报警统计定时中断次数的延时计数器

unsigned int uiVoiceCnt=0; //蜂鸣器鸣叫的持续时间计数器

unsigned char ucLock=0; //互斥量,俗称原子锁

void main()

{

initial_myself();

delay_long(100);

initial_peripheral();

while(1)

{

alarm_run(); //报警器定时报警

}

}

/* 注释一:

* 保护多线程共享全局变量的原理:

* 多个线程同时访问同一个全局变量,如果都是读取操作,则不会出现问题。如果一个线程负责改变此变量的值,

* 而其他线程负责同时读取变量内容,则不能保证读取到的数据是经过写线程修改后的。

* 鸿哥的基本程序框架都是两线程为主,一个是main函数线程,一个是定时函数线程。

*/

void alarm_run() //报警器的应用程序

{

switch(ucAlarmStep)

{

case 0:

if(uiTimeAlarmCnt>=const_time_3s) //时间到

{

/* 注释二:

* 用关中断来保护多线程共享的全局变量:

* 因为uiTimeAlarmCnt和uiVoiceCnt都是unsigned int类型,本质上是由两个字节组成。

* 在C语言中uiTimeAlarmCnt=0和uiVoiceCnt=const_voice_short看似一条指令,

* 实际上经过编译之后它不只一条汇编指令。由于另外一个定时中断线程里也会对这个变量

* 进行判断和操作,如果不禁止定时中断或者采取其它措施,定时函数往往会在主函数还没有

* 结束操作共享变量前就去访问或处理这个共享变量,这就会引起冲突,导致系统运行异常。

*/

ET0=0; //禁止定时中断

uiTimeAlarmCnt=0; //时间计数器清零

uiVoiceCnt=const_voice_short; //蜂鸣器短叫

ET0=1; //开启允许定时中断

ucAlarmStep=1; //切换到下一个步骤

}

break;

case 1:

if(uiTimeAlarmCnt>=const_time_6s) //时间到

{

/* 注释三:

* 用互斥量来保护多线程共享的全局变量:

* 我觉得,在这种场合,用互斥量比前面用关中断的方法更加好。

* 因为一旦关闭了定时中断,整个中断函数就会在那一刻停止运行了,

* 而加一个互斥量,既能保护全局变量,又能让定时中断函数正常运行,

* 真是一举两得。

*/

ucLock=1; //互斥量加锁。 俗称原子锁

uiTimeAlarmCnt=0; //时间计数器清零

uiVoiceCnt=const_voice_long; //蜂鸣器长叫

ucLock=0; //互斥量解锁。 俗称原子锁

ucAlarmStep=0; //返回到上一个步骤

}

break;

}

}

void T0_time() interrupt 1

{

TF0=0; //清除中断标志

TR0=0; //关中断

if(ucLock==0) //互斥量判断

{

if(uiTimeAlarmCnt<0xffff) //设定这个条件,防止uiTimeAlarmCnt超范围。

{

uiTimeAlarmCnt++; //报警的时间计数器,累加定时中断的次数,

}

if(uiVoiceCnt!=0)

{

uiVoiceCnt--; //每次进入定时中断都自减1,直到等于零为止。才停止鸣叫

beep_dr=0; //蜂鸣器是PNP三极管控制,低电平就开始鸣叫。

}

else

{

; //此处多加一个空指令,想维持跟if括号语句的数量对称,都是两条指令。不加也可以。

beep_dr=1; //蜂鸣器是PNP三极管控制

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top