volatile类型修饰符

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/baobingji/article/details/84927918

volatile是一个类型修饰符,其作用是作为指令关键字,确保本条指令不会因为编译器的优化而省略,且要求每次直接读值。简单来说,就是防止编译器对代码进行优化。
eg:
a[1]=1;
a[1]=2;
a[1]=3;
a[1]=4;
对于外部硬件而言,上述四条语句分别表示不同的操作,会产生不同的动作,但是编译器却会对上述四条语句进行优化,认为只有a[1]=4有效;如果加入关键字volatile,则编译器会逐一的进行编译并产生相应的机器代码。


经常使用到volatile变量的例子:
(1)并行设备的硬件寄存器(如状态寄存器);
(2)一个中断服务子程序中会访问到的非自动变量;
(3)多线程应用中被几个任务共享的变量;
嵌入式系统程序经常同硬件、中断、RTOS等等打交道,所有这些都要求使用volatile变量。


如在C语言中,volatile关键字可以用来提醒编译器它后面所定义的变量随时有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。

volatile与多线程语义
临界区内部,通过互斥锁(mutex)保证只有一个线程可以访问,因此临界区内的变量不需要是volatile的;而在临界区外部,被多个线程访问的变量应为volatile,这也符合了volatile的原意:防止编译器缓存(cache)了被多个线程并发用到的变量。volatile对象只能调用volatile成员函数,这意味着应仅对多线程并发安全的成员函数加volatile修饰,这种volatile成员函数可自由用于多线程并发或者重入而不必使用临界区;非volatile的成员函数意味着单线程环境,只应在临界区内调用。在多线程编程中可以令该数据对象的所有成员函数均为普通的非volatile修饰,从而保证了仅在进入临界区(即获得了互斥锁)后把该对象显式转为普通对象之后才能调用该数据对象的成员函数。这种用法避免了编程者的失误——在临界区以外访问共享对象的内容:

template <typename T> class LockingPtr{
  public:
    LockingPtr(volatile T& obj, Mutex& mtx)
        :pObj_(const_cast<T*>(&obj) ),  pMtx_(&mtx)
        {  mtx.Lock();  }
    ~LockingPtr()
        { pMtx->Unlock();  }
    T& operator*()
        {  return *pObj_;  }
    T* operator->()
        {  return pObj_;   }
  private:
    T* pObj_;
    Mutex* pMtx_;
    LockingPtr(const LockingPtr&);
    LockingPtr& operator=(const LockingPtr&);
}

对于内建类型,不应直接用volatile,而应把它包装为结构的成员,就可以保护了volatile的结构对象不被不受控制地访问.


C语言的例子:

static int foo;
  
void bar(void) {
    foo = 0;
  
    while (foo != 255)
         ;
}

在这里例子中,代码将foo的值设置为0。然后开始不断地轮询它的值直到它变成255.

void bar_optimized(void) {
    foo = 0;
  
    while (true)
         ;
}

一个执行优化的编译器会提示没有代码能修改foo的值,并假设它永远都只会是0.因此编译器将用类似下列的无限循环替换函数体.

static volatile int foo;
  
void bar (void) {
    foo = 0;
  
    while (foo != 255)
        ;
}

但是,foo可能指向一个随时都能被计算机系统其他部分修改的地址,例如一个连接到中央处理器的设备的硬件寄存器,上面的代码永远检测不到这样的修改。如果不使用volatile关键字,编译器将假设当前程序是系统中能改变这个值部分(这是到最广泛的一种情况)。 为了阻止编译器像上面那样优化代码,需要使用volatile关键字,这样修改以后循环条件就不会被优化掉,当值改变的时候系统将会检测到。


猜你喜欢

转载自blog.csdn.net/baobingji/article/details/84927918