向字段中添加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仍必须用于这些平台上的安全多线程访问。