我在文件范围中使用了静态全局变量和静态volatile变量,
两者都由ISR和主循环更新,主循环检查变量的值.
在优化期间,全局变量和volatile变量都不会被优化.因此,全局变量不是使用volatile变量来解决问题.
那么使用全局变量而不是volatile是否合适呢?
使用静态volatile的任何具体原因??
任何示例程序都是可观的.
提前致谢..
首先让我提一下静态全局变量与全局变量相同,只是您将变量限制在文件的范围内.也就是说,你不能通过extern
关键字在其他文件中使用这个全局变量.
因此,您可以将问题减少到全局变量和volatile变量.
现在变为volatile:
比如const
,volatile
是一个类型修饰符.
该volatile
关键字是为了防止编译器优化,可以使代码不正确,特别是当有异步事件.
声明为的对象volatile
可能无法在某些优化中使用.
系统始终在使用它的位置读取易失性对象的当前真值,即使前一条指令要求来自同一对象的值也是如此.此外,在分配时立即写入对象的值.这意味着没有将易失性变量缓存到CPU寄存器中.
Jobb博士有一篇关于不稳定的文章.
以下是Jobb博士文章的一个例子:
class Gadget { public: void Wait() { while (!flag_) { Sleep(1000); // sleeps for 1000 milliseconds } } void Wakeup() { flag_ = true; } ... private: bool flag_; };
如果编译器看到这Sleep()
是一个外部调用,它将假定Sleep()
不可能更改变量flag_的值.因此编译器可以将值存储flag_
在寄存器中.在这种情况下,它永远不会改变.但是如果另一个线程调用wakeup,第一个线程仍在从CPU的寄存器中读取.Wait()
永远不会醒来.
那么为什么不直接将变量缓存到寄存器中并完全避免这个问题呢?事实证明,这种优化可以真正为您节省大量时间.因此,C/C++允许您通过volatile
关键字明确禁用它.
上面的事实flag_
是成员变量,而不是全局变量(也不是静态全局变量)并不重要.即使您处理全局变量(和静态全局变量),示例后面的解释也会给出正确的推理.
一个常见的误解是声明变量volatile
足以确保线程安全.变量的操作仍然不是原子的,即使它们没有在寄存器中"缓存"
指针易失性:
使用指针进行易失性,像const一样使用指针.
类型变量volatile int *
意味着指针指向的变量是易失性的.
类型变量int * volatile
意味着指针本身是易失性的.
他们是不同的东西.我不是volatile语义的专家.但我认为这里描述的是有道理的.
全局只是意味着有问题的标识符在文件范围内声明.有不同的范围,称为函数(其中定义了goto-labels),文件(其中包含全局变量),块(正常局部变量驻留)和函数原型(函数参数驻留).这个概念只是用于构建标识符的可见性.它与优化没有任何关系.
static
是一个存储持续时间(我们不会在这里查看)和一种在文件范围内部链接中声明名称的方法.这可以针对仅在一个翻译单元内所需的功能或对象来完成.典型示例可能是help
打印出接受的参数的函数,并且仅从main
同一.c
文件中定义的函数调用.
C99草案中的6.2.2/2:
如果对象或函数的文件范围标识符声明包含特定于静态的存储类,则标识符具有内部链接.
内部链接意味着标识符在当前翻译单元外部不可见(如help
上面的功能).
易失性是另一回事:(6.7.3/6)
具有挥发性限定类型的对象可以以实现未知的方式修改或具有其他未知的副作用.因此,任何涉及这种对象的表达都应严格按照抽象机的规则进行评估,如5.1.2.3所述.此外,在每个序列点,最后存储在对象中的值应与抽象机器规定的值一致,除非由前面提到的未知因素修改.
标准为volatile
冗余(5.1.2.3/8)的示例提供了一个很好的示例:
实现可以定义抽象语义和实际语义之间的一对一对应关系:在每个序列点,实际对象的值将与抽象语义指定的值一致.关键字
volatile
将是多余的.
序列点是完成关于抽象机器的副作用的影响的点(即,不包括诸如存储器单元值的外部条件).右侧和左侧之间&&
和||
之后,;
并从函数调用返回的例如序列点.
在抽象的语义是什么,编译器可以从特定的程序中看到的代码只序列推断.优化的效果在这里无关紧要.实际语义包括通过写入对象(例如,更改存储器单元)完成的副作用的影响.将对象限定为volatile意味着总是直接从内存中获取对象的值("由未知因素修改").标准没有在任何地方提到线程,如果您必须依赖更改的顺序或操作的原子性,您应该使用平台相关的方法来确保.
为了便于理解,intel 在这里有一篇很棒的文章.
继续将文件范围(全局)数据声明为volatile.全局数据本身并不意味着变量的值将等于存储在内存中的值.静态只会使你的对象在当前翻译单元本地(当前.c
文件和所有其他文件#include').
"volatile"关键字表明编译器不对涉及该变量的代码进行某些优化; 如果您只使用全局变量,则不会阻止编译器错误地优化您的代码.
例:
#define MYPORT 0xDEADB33F volatile char *portptr = (char*)MYPORT; *portptr = 'A'; *portptr = 'B';
没有"易失性",可以优化第一次写入.