标题: 两个线程读写1个变量需要保护吗?一个是中断任务,一个是普通任务 [打印本页]

作者: azjdly    时间: 2022-3-30 21:43
标题: 两个线程读写1个变量需要保护吗?一个是中断任务,一个是普通任务
比如一个变量,有两个线程读写,比如说一个是中断任务,一个是普通任务,直接用volatile行吗?
作者: Hephaestus    时间: 2022-3-30 23:13
这就是原子操作的概念。
以八位机为例,主循环:
int i;
i++;
对于8位机i是16位的,这个i++会分解成两个操作:
i.low++;
i.high+=CY;
设i=255,那么实际操作就是:
i.low++; //i.low=0
i.high+=CY;  //i.high=1
这个代码正常工作的话,i++的结果将是i=0x100也就是i=256,这个结果是正确的。

但是如果某个中断程序也要读取i的值,在i.low++之后发生了中断,那么中断读到的i的值将是一个极其不合理的0!这会导致中断处理程序发生异常操作!

所以,只要不是原子操作,主循环和中断共享变量的话,一定要关闭中断防止非原子操作被中断打断!上面代码正确的写法是:

IE=0; //关中断
i++;
IE=1; //开中断
作者: azjdly    时间: 2022-3-30 23:33
Hephaestus 发表于 2022-3-30 23:13
这就是原子操作的概念。
以八位机为例,主循环:
int i;

大佬,如果是32位,不存在这个高低位情况下,单片机把一个变量加1,是先把变量从内存取出到寄存器,让后把寄存器这个数加1,类似ADD R1 1这样的指令,让后放回内存里,是这个流程吗?

如果先从内存取变量值为3到寄存器这个过程后,就发生了中断,中断里改变了内存里这个变量的值为5,然后中断结束,任务恢复,让后任务把寄存器里的3加1变成4,再放回内存,那么是不是这个变量的值就会发生错误?
作者: Hephaestus    时间: 2022-3-31 00:37
azjdly 发表于 2022-3-30 23:33
大佬,如果是32位,不存在这个高低位情况下,单片机把一个变量加1,是先把变量从内存取出到寄存器,让后 ...

单一的机器指令不会被打断,所以不需要考虑原子操作问题。

主贴我忘了回答volatile问题,加不加volatile关键字跟原子操作没任何关系。
作者: yzwzfyz    时间: 2022-3-31 08:50
只能依据你的需要判定。
作者: wojiaoguogai    时间: 2022-3-31 09:05
读的话不需要保护,写是要保护的,volatile也没用
作者: Y_G_G    时间: 2022-3-31 15:55
赞同5楼的说法
要看你自己的需要去操作
一个普通任务,一个中断,操作同一个变量,有可能出现的情况就是:你要普通任务中操作到一半的时候,中断又去对这个变量进行操作,这就容易出现混乱
不管是8位机还是32位,都是一样的,你都不知道你的代码最后会被编译成什么,就算是32位,你也不能保证你的代码是一个时钟就能完成的
所以,只能根据你自己的需要来操作
作者: a110ddwy    时间: 2022-3-31 16:04
中断会导致挂起当前操作,从正常设计上来说你这个一般是需要去读当前这个变量的,写的话一般设计阶段就得避免了,至于你描述的情况,除非你设计的时候就要做这样的操作,否则就是设计上的bug。
这个应该按照楼上几位大佬说的,根据你自己的需求从设计上进行操作。
然后是volatile的限定符的目的是警告编译器不要进行假定的优化。主要是为了避免编译器优化导致莫名其妙的问题的。
所以,最后就又回到你的需求上了。
作者: Hephaestus    时间: 2022-4-1 17:26
Y_G_G 发表于 2022-3-31 15:55
赞同5楼的说法
要看你自己的需要去操作
一个普通任务,一个中断,操作同一个变量,有可能出现的情况就是:你 ...

以我在沙发位的8位机例子为例,如果i是char型的,那么i++就是原子操作,无需保护,主循环可以随便改,中断也可以随便读。

什么是原子操作?原子操作只有2种状态,一种是没做,一种是做完了,看不到正在做的状态。你连代码会编译成什么都不知道还是不要误导他人了。
作者: Y_G_G    时间: 2022-4-1 17:30
Hephaestus 发表于 2022-4-1 17:26
以我在沙发位的8位机例子为例,如果i是char型的,那么i++就是原子操作,无需保护,主循环可以随便改,中 ...

对的对的,你说的是对的




欢迎光临 (http://www.51hei.com/bbs/) Powered by Discuz! X3.1