C# 语言volatile关键字

示例

向字段中添加volatile关键字向编译器表明该字段的值可能会被多个单独的线程更改。volatile关键字的主要目的是防止只采用单线程访问的编译器优化。使用volatile可以确保字段的值是可用的最新值,并且该值不受非volatile值的缓存的影响。

最好将多个线程可能使用的每个变量标记为volatile,以防止由于幕后优化而出现意外行为。考虑以下代码块:

public class Example
{
    public int x;

    public void DoStuff()
    {
        x = 5;

        // 编译器会将其优化为y = 15
        var y = x + 10;

        /*x的值始终是当前值,但y始终是 "15" */
        Debug.WriteLine("x = " + x + ", y = " + y);
    }    
}

在上面的代码块中,编译器读取语句x=5和y=x+10,并确定y的值总是以15结束。因此,它将优化最后一条语句,使y=15。但是,变量x实际上是一个公共字段,可以在运行时通过单独作用于该字段的不同线程修改x的值。现在考虑这个修改过的代码块。请注意,字段x现在声明为volatile。

public class Example
{
    public volatile int x;

    public void DoStuff()
    {
        x = 5;

        // 编译器不再优化此语句
        var y = x + 10;

        /* the value of x and y will always be the correct values */
        Debug.WriteLine("x = " + x + ", y = " + y);
    }    
}

现在,编译器将查找该字段的读取用法,x并确保始终检索该字段的当前值。这样可以确保即使有多个线程正在读取和写入该字段,x也始终会检索到的当前值。

volatile只能在class或struct内的字段上使用。以下是正确的:

public void MyMethod(){    
   volatile int x;
}

volatile 只能应用于以下类型的字段:

  • 引用类型或已知为引用类型的通用类型参数

  • 原始类型如sbyte,byte,short,ushort,int,uint,char,float,和bool

  • 枚举类型的基础上byte,sbyte,short,ushort,int或者uint

  • IntPtr 和 UIntPtr


备注:

  • volatile修饰符通常用于由多个线程而不使用lock语句能够连续访问访问的领域。

  • volatile关键字可以被应用到参考类型的字段

  • volatile关键字将无法在32位平台atomic上的64位基元上进行操作。互锁操作(例如Interlocked.Read和)Interlocked.Exchange仍必须用于这些平台上的安全多线程访问。