编辑:问题已经被大量重写
为了回答这个问题,我深入研究了一下volatile
和Interlocked
的一些事情,发现了一些我之前不知道的内容。让我们澄清一下这个问题,不仅是为了我自己,也是为了这个讨论和其他正在学习这方面知识的人们:
volatile
读/写应该不受重新排序的影响。这只是指读和写操作,而不是任何其他操作;Interlocked
使用原子汇编指令进行CompareExchange (cmpxchg
)、Increment (inc
)等操作;Interlocked
有时会使用锁:在多处理器系统上使用硬件锁; 在单处理器系统中,没有硬件锁;Interlocked
与volatile
的区别在于它使用了一个全屏障,而volatile使用了半屏障。volatile
时,写后读操作可能会被重新排序。这在使用Interlocked
时不可能发生。`VolatileRead和VolatileWrite
与volatile有相同的重新排序问题(感谢Brian Gideon提供的链接)。现在我们有了规则,我们可以对你的问题给出一个答案:
volatile
做而不能用Interlocked
做的事情:
a = b
,其中a
或b
是volatile,但这很明显;Interlocked
中是不可能的。换句话说,你可以使用volatile
比使用Interlocked
更不安全。volatile
比Interlocked
更快。从语义上讲:不,因为Interlocked
仅提供了一组操作的超集,并且更安全,因为它应用了全屏障。你不能使用volatile
做任何Interlocked
不能做的事情,而你可以使用Interlocked
做许多volatile不能做的事情:
static volatile int x = 0;
x++; // non-atomic
static int y = 0;
Interlocked.Increment(y); // atomic
作用域: 是的,将变量声明为volatile
会使得每次访问都是volatile的。除此之外,无法以其他方式强制这种行为,因此volatile
不能被Interlocked
替换。在其他库、接口或硬件可以随时访问并更新您的变量,或者需要最新版本的情况下,这是必需的。
如果你问我,这个最后一部分才是volatile
的实际需求,并且当两个进程共享内存并需要读取或写入而不加锁时,它可能是理想的选择。在这种情况下,将变量声明为volatile
比强制所有程序员使用Interlocked
更安全(编译器无法强制执行)。
编辑:以下引用是我的原始回答的一部分,我将其保留;-)
来自C# 程序设计语言标准的引用:
对于非 volatile 字段,考虑重新排序指令的优化技术可能会导致在没有同步的情况下访问字段的多线程程序中出现意外和不可预测的结果,例如由lock语句 提供的同步。这些优化可以由编译器、运行时系统或硬件执行。对于 volatile 字段,这种重新排序优化是受限制的:
读取 volatile 字段称为volatile读。volatile读具有“获取语义”,即在指令序列中它之后发生的任何内存引用都保证在其之前。
写入 volatile 字段称为volatile写。volatile写具有“释放语义”,即在指令序列中在写入指令之前的任何内存引用都保证已发生。
更新:问题大部分重写,纠正了我的原始回答并添加了一个“真实”的答案
CompareExchange
(认为它不是原子操作),SSCLI(共享源代码CLI)显示它最终转换为x86处理器上的cmpxchg
汇编指令,而该指令就像Interlocked
中的其他任何内容一样,确实是原子操作。 - Abel这是一个相当复杂的话题。我发现Joseph Albahari的写作是.NET Framework中多线程概念方面更为明确和准确的来源之一,可能有助于回答您的问题。
但是,简单地总结一下,volatile
关键字和Interlocked
类在它们的使用方式上有很多重叠。当然,它们都远远超出了普通变量所能做到的。
可以直接查看这个值。
只要您仅使用Interlocked类来访问变量,那么就没有区别。 volatile 的作用是告诉编译器该变量是特殊的,并且在优化时不应假定该值没有更改。
看看这个循环:
bool done = false;
...
while(!done)
{
... do stuff which the compiler can prove doesn't touch done...
}
如果你在另一个线程中将done
设置为true
,你会期望循环退出。然而,如果没有将done
标记为volatile
,那么编译器有可能意识到循环代码永远不会改变done
,并且可以优化掉用于退出的比较。
这是多线程编程中的难点之一 - 许多问题只会在特定情况下出现。
我不会试图成为这个主题的权威,但我强烈建议您查看备受推崇的Jon Skeet所写的this article。
还要看一下this answer的最后一部分,其中详细说明了应该使用volatile
的情况。
是的,您可以使用volatile变量而不是锁来提高一些性能。
锁是一个完整的内存屏障,它可以给您与volatile变量相同的特性以及许多其他特性。正如已经说过的,volatile只是确保在多线程场景中,如果CPU更改其缓存行中的值,其他CPU会立即看到该值,但并不保证任何锁定语义。
问题是锁比volatile强大得多,您应该在可能的情况下使用volatile来避免不必要的锁定。