挥发物
将 volatile
关键字添加到字段会向编译器指示字段的值可能会被多个单独的线程更改。volatile
关键字的主要目的是防止仅采用单线程访问的编译器优化。使用 volatile
可确保字段的值是可用的最新值,并且该值不受非易失性值的缓存限制。
最好将多个线程可能使用的每个变量标记为 volatile
,以防止由于幕后优化而导致的意外行为。请考虑以下代码块:
public class Example
{
public int x;
public void `DoStuff()`
{
x = 5;
// the compiler will optimize this to y = 15
var y = x + 10;
/* the value of x will always be the current value, but y will always be "15" */
Debug.WriteLine("x = " + x + ", y = " + y);
}
}
在上面的代码块中,编译器读取语句 x = 5
和 y = x + 10
,并确定 y
的值总是最终为 15.因此,它将优化最后一个语句为 y = 15
。然而,变量 x
实际上是 public
字段,并且 x
的值可以在运行时通过分别作用于该字段的不同线程来修改。现在考虑这个修改过的代码块。请注意,字段 x
现在声明为 volatile
。
public class Example
{
public volatile int x;
public void `DoStuff()`
{
x = 5;
// the compiler no longer optimizes this statement
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
es 或 struct
s 中的字段。下面是不是有效的 :
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 位平台原子上对 64 位基元进行操作。Interlocked.Read
和Interlocked.Exchange
等联锁操作仍必须用于这些平台上的安全多线程访问。