与使用Interlocked类相比,使用volatile关键字有什么好处吗?

时间:2009-11-09 14:15:10

标签: c# .net multithreading volatile interlocked

换句话说,我可以使用常规变量和Interlocked类无法解决的volatile变量吗?

5 个答案:

答案 0 :(得分:16)

编辑:大部分重写的问题

为了回答这个问题,我在这个问题上进一步深入了解了一些我不知道的关于volatileInterlocked的事情。让我们清楚地了解这一点,不仅仅是为了我,而是为了讨论和其他人阅读:

  • volatile读/写应该不受重新排序的影响。此表示读取和写入,表示任何其他操作;
  • volatility 不强制在CPU上,即硬件级别(x86在任何读/写上使用获取和释放围栏)。它确实阻止了编译器或CLR优化;
  • Interlocked使用原子汇编指令进行CompareExchange(cmpxchg),增量(inc)等;
  • Interlocked 有时会使用锁定:hardware lock on multi processor systems;在单处理器系统中,没有硬件锁;
  • Interlockedvolatile的不同之处在于它使用完整围栏,其中volatile使用半围栏。
  • 使用volatile时,
  • A read following a write can be reorderedInterlocked不会发生这种情况。 VolatileReadVolatileWrite具有与`volatile相同的重新排序问题(链接感谢Brian Gideon)。

现在我们有了规则,我们可以为您的问题定义答案:

  • 从技术上讲:是的,您可以使用volatile执行Interlocked无法做到的事情:
    1. 语法:你不能写a = b ab是易变的,但这很明显;
    2. 由于重新排序,您可以在将其写入volatile变量后读取不同的值。您无法使用Interlocked执行此操作。换句话说:volatile您可以更少安全,然后您就可以Interlocked
    3. 效果:volatileInterlocked快。
  • 语义上:不,因为Interlocked只提供操作的超集,并且使用起来更安全,因为它应用了完整的防护。使用volatile时无法对Interlocked执行任何操作,并且您可以使用Interlocked进行很多,而不能使用volatile:

    static volatile int x = 0;
    x++;                        // non-atomic
    static int y = 0;
    Interlocked.Increment(y);   // atomic
    
  • 范围:是的,声明变量volatile会使每次访问都变得不稳定。不可能以任何其他方式强制执行此行为,因此volatile不能替换为Interlocked。在其他库,接口或硬件可以访问您的变量并随时更新它或需要最新版本的情况下,需要这样做。

如果您问我,最后一点是volatile的实际需要,并且可能使其成为两个进程共享内存并且需要在没有锁定的情况下进行读取或写入的理想选择。将变量声明为volatile在此上下文中更安全,然后强制所有程序员使用Interlocked(编译器无法强制执行)。


编辑:以下引用是我原来答案的一部分,我会留下来的; - )

来自 C#编程语言标准的引用:

  

对于非易失性字段,优化   考虑重新排序的技术   说明可能导致意外   和不可预知的结果   访问的多线程程序   没有同步的字段,例如   由 lock-statement 提供的。   这些优化可以通过执行   编译器,由运行时系统,   或者通过硬件。对于易变场,   这种重新排序优化是   限制:

     
      
  • 读取易失性字段称为 易失性读取 。易失性读   具有:获取语义“;即它   保证在任何之前发生   对之后发生的内存的引用   它在指令序列中。

  •   
  • 写入易失性字段称为 易失性写入 。一个   volatile写有“释放   语义“;也就是说,它是有保证的   在任何内存引用之后发生   在写入指令之前   指令序列。

  •   

更新: 在很大程度上重写了问题,纠正了原来的回复,并添加了“真实”答案

答案 1 :(得分:1)

这是一个相当复杂的话题。我发现Joseph Albahari的writeup是.NET Framework中多线程概念的更明确和准确的来源之一,可能有助于回答您的问题。

但是,为了快速总结,volatile关键字和Interlocked类之间有很多重叠,只要它们如何使用。当然,这两种方法都超出了普通变量的范围。

答案 2 :(得分:0)

是的 - 您可以直接查看该值。

只要您使用Interlocked类来访问变量,就没有区别。 volatile 的作用是告诉编译器该变量是特殊的,在优化时不应该假设该值没有改变。

采取这个循环:

bool done = false;

...

while(!done)
{
... do stuff which the compiler can prove doesn't touch done...
}

如果在另一个线程中将done设置为true,您可能希望循环退出。但是 - 如果完成没有标记为volatile,那么编译器可以选择认识到循环代码永远不会改变done,并且它可以优化退出比较。

这是多线程编程的难点之一 - 许多问题只会在某些情况下出现。

答案 3 :(得分:0)

我不会试图成为这个主题的权威,但我强烈建议您通过吹嘘的Jon Skeet来看看this article

另请参阅this answer的最后部分,其中详细说明volatile应该用于哪些内容。

答案 4 :(得分:-1)

是的,你可以通过使用volatile变量而不是锁来获得一些性能。

Lock是一个完整的内存屏障,它可以为您提供与volatile变量相同的特性以及许多其他变量。正如已经说过的那样,volatile只是确保在多线程场景中,如果CPU更改其缓存行中的值,其他CPU会立即看到该值,但根本不确保任何锁定语义。

事情是锁定比volatile更强大,你应该使用volatile来避免不必要的锁定。