在C#中,
volatile
关键字涉及到读/写重排,因此它是一些相当深奥的东西。来自
这里(我认为这是有关线程的“圣经”之一)的内容如下:
volatile
关键字指示编译器在每次从该字段读取时生成一个获取栅栏,并在每次写入该字段时生成一个释放栅栏。获取栅栏可以防止其他读/写在栅栏之前被移动;释放栅栏可以防止其他读/写在栅栏之后被移动。这些“半栅栏”比全栅栏更快,因为它们给运行时和硬件更多的优化空间。
这很难懂 :-)
现在... 它不意味着:
它只是意味着如果您从一个易失性变量中读取某些内容,则在此“特殊”读取之前已读/写入的所有其他内容都不会在此“特殊”读取之后被移动。所以它创建了一个屏障。因此,具有讽刺意味的是,通过从易失性变量中读取,您保证在读取时对任何其他变量(易失性或非易失性)所做的所有写操作都将完成。
易失性写入可能更重要,这在某种程度上是由英特尔CPU保证的,并且是Java的第一个版本没有保证的内容:无写入重排。问题是:
object myrefthatissharedwithotherthreads = new MyClass(5)
在哪里
class MyClass
{
public int Value;
MyClass(int value)
{
Value = value;
}
}
现在...那个表达式可以想象成:
var temp = new MyClass();
temp.Value = 5;
myrefthatissharedwithotherthreads = temp;
这里的temp
是编译器生成的你看不见的内容。
如果写操作可以被重新排序,那么可能会出现以下情况:
var temp = new MyClass();
myrefthatissharedwithotherthreads = temp;
temp.Value = 5;
另一个线程可能会看到一个部分初始化的MyClass
,因为在类MyClass
完成初始化之前,myrefthatissharedwithotherthreads
的值是可读的。
volatile
防止变量访问周围的指令重排。这是相当复杂的。请参见 http://www.albahari.com/threading/part4.aspx#_Memory_Barriers_and_Volatility - xanatos