【问题标题】:Why make a bool volatile when multithreading in C#?为什么在 C# 中进行多线程时使 bool 变得易变?
【发布时间】:2015-05-16 06:23:56
【问题描述】:

我非常了解 C++ 中的 volatile 关键字。但在 C# 中,它似乎具有不同的含义,更多地与多线程相关。我认为 bool 操作是原子的,并且我认为如果操作是原子的,您将不会有线程问题。我错过了什么?

https://msdn.microsoft.com/en-us/library/x13ttww7.aspx

【问题讨论】:

标签: c# multithreading


【解决方案1】:

我认为 bool 操作是原子的

它们确实是原子的。

我认为如果操作是原子的,你就不会有线程问题。

那是您的图片不完整的地方。想象一下,您有两个线程在不同的内核上运行,每个内核都有自己的缓存层。线程#1 的缓存中有foo,线程#2 更新foo 的值。除非foo 被标记为volatile、获得lock、使用Interlocked 类或显式调用Thread.MemoryBarrier(),否则线程#1 不会看到更改,这将导致缓存中的值无效.因此,guaranteeing that you read the most up to date value:

使用 volatile 修饰符可确保一个线程检索另一个线程写入的最新值。

Eric Lippert 有一个关于波动性的 great post,他解释说:

易失性读取的真正语义 并且写入比我在这里概述的要复杂得多;在 事实上,他们 实际上并不能保证每个处理器都会停止它所执行的操作 正在执行和更新缓存到主内存/从主内存更新。 相反,它们提供 关于在读取之前和之后如何访问内存的较弱保证 可以观察到写入是相对于彼此排序的。

编辑:

根据@xanatos 评论,这并不意味着volatile 保证立即读取,它保证读取最新值。

【讨论】:

  • 部分错误。 volatile 写入并不能保证立即写入。
  • @xanatos 我并不是说它保证立即写入。我保证它可以读取更新的值。
  • 你在写 volatile 时遇到问题 :-) 写了两次,错了两次 :-) :-)
  • @xanatos 废话:X。固定。
【解决方案2】:

C# 中的volatile 关键字是关于读/写重新排序的,所以它是相当深奥的东西。

http://www.albahari.com/threading/part4.aspx#_Memory_Barriers_and_Volatility

(我认为这是关于线程的“圣经”之一)写道:

volatile 关键字指示编译器在每次读取该字段时生成一个获取栅栏,并在每次写入该字段时生成一个释放栅栏。获取栅栏防止其他读/写在栅栏之前移动;释放栅栏防止其他读/写在栅栏之后移动。这些“半栅栏”比全栅栏更快,因为它们为运行时和硬件提供了更大的优化空间。

这是非常难以理解的东西:-)

现在......这不是什么意思:

  • 这并不意味着现在将读取值/现在将写入

它只是意味着如果您从 volatile 变量中读取某些内容,则在此“特殊”读取之前已读取/写入的所有其他内容将不会在此“特殊”读取之后移动。所以它创造了一个障碍。因此自相矛盾的是,通过读取 volatile 变量,您可以保证在读取时对任何其他变量(无论是否为 volatile)所做的所有写入都将完成。

易失性写入可能更重要,并且部分由 Intel 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,因为myrefthatissharedwithotherthreads 的值在MyClass 类完成初始化之前是可读的。

【讨论】:

  • 不仅仅是栅栏,volatile 还可以防止诸如常量传播、死代码消除或寄存器提升等优化。它比访问周围的简单栅栏更具限制性。
  • @Cicada 你有这方面的 C# 参考吗?
猜你喜欢
  • 2014-07-16
  • 1970-01-01
  • 1970-01-01
  • 2012-10-19
  • 1970-01-01
  • 2011-04-22
  • 1970-01-01
  • 2014-02-17
  • 1970-01-01
相关资源
最近更新 更多