Volatile变量

在程序设计中，尤其是在C语言、C++、C#和Java语言中，使用volatile关键字声明的变量或对象通常具有与优化、多线程相关的特殊属性。通常，volatile关键字用来阻止（伪）编译器认为的无法“被代码本身”改变的代码（变量/对象）进行优化。如在C语言中，volatile关键字可以用来提醒编译器它后面所定义的变量随时有可能改变，因此编译后的程序每次需要存储或读取这个变量的时候，都会直接从变量地址中读取数据。如果没有volatile关键字，则编译器可能优化读取和存储，可能暂时使用寄存器中的值，如果这个变量由别的程序更新了的话，将出现不一致的现象。
在C环境中，volatile关键字的真实定义和适用范围经常被误解。虽然C++、C#和Java都保留了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语言中MMIO的例子

在这里例子中，代码将foo的值设置为0。然后开始不断地轮询它的值直到它变成255：
static int foo;
 
void bar(void) {
    foo = 0;
 
    while (foo != 255)
         ;}
一个执行优化的编译器会提示没有代码能修改foo的值，并假设它永远都只会是0.因此编译器将用类似下列的无限循环替换函数体：
void bar_optimized(void) {
    foo = 0;
 
    while (true)
         ;}
但是，foo可能指向一个随时都能被计算机系统其他部分修改的地址，例如一个连接到中央处理器的设备的硬件暂存器，上面的代码永远检测不到这样的修改。如果不使用volatile关键字，编译器将假设当前程序是系统中唯一能改变这个值部分（这是到目前为止最广泛的一种情况）。 为了阻止编译器像上面那样优化代码，需要使用volatile关键字：
static volatile int foo;
 
void bar (void) {
    foo = 0;
 
    while (foo != 255)
        ;}
这样修改以后循环条件就不会被优化掉，当值改变的时候系统将会检测到。

Ada中的volatile

在Ada中，比起关键字，Volatile标记更像是一种指令。“对于volatile对象而言，所有读和更新都会作为一个整体直接执行到内存”。