挥发性违反其主要工作?

根据MSDN :

volatile关键字表示某个字段可能被同时执行的多个线程修改。 声明为volatile的字段不受编译器优化的约束,这些优化假定单个线程进行访问。 这可确保始终在字段中显示最新值。

请注意最后一句:

这可确保始终在字段中显示最新值。

但是,此关键字存在问题。

我已经读过它可以改变指令的顺序:

First instruction Second instruction Can they be swapped? Read Read No Read Write No Write Write No Write Read Yes! <---- 

这意味着约翰将一个值设置为一个易变的字段, 后来保罗想要阅读该字段,保罗正在获得旧的价值!

这是怎么回事? 这不是主要的工作吗?

我知道还有其他解决方案,但我的问题是关于volatile关键字。

我(作为程序员)是否需要阻止使用此关键字 – 因为这种奇怪的行为?

MSDN文档是错误的。 这肯定不是volatile 。 C#规范确切地告诉你volatile作用,并且获得“新读”或“提交写入”不是其中之一。 规范是正确的。 volatile只保证读取时的获取范围和写入时的释放范围。 这些定义如下。

  • acquire-fence:一种内存屏障,其中不允许其他读写操作在围栏之前移动。
  • release-fence:一种内存屏障,在屏障不允许其他读写操作。

我将尝试使用箭头符号解释表格。 ↓箭头将标记易失性读数,↑箭头将标记易失性写入。 没有指令可以通过箭头移动。 把箭头想象成推开一切。

在下面的分析中我将使用变量; xy 。 我还假设它们被标记为volatile

情况1

注意在读取x之后箭头的放置如何防止读取y向上移动。 另请注意,在这种情况下, y的波动性无关紧要。

 var localx = x; ↓ var localy = y; ↓ 

案例#2

注意在读取x之后箭头的放置如何防止写入y向上移动。 另请注意,在这种情况下, xy的任何一个的波动性,但不是两者的波动性都可以省略。

 var localx = x; ↓ ↑ y = 1; 

案例#3

注意在写入y之前箭头的放置如何防止写入x向下移动。 请注意,在这种情况下, x的波动性无关紧要。

 ↑ x = 1; ↑ y = 2; 

案例#4

请注意,写入x和读取y之间没有障碍。 因此,写入x可以向下浮动,或者y的读取可以向上浮动。 这两种运动都是有效的。 这就是为什么可以交换写 – 读案例中的指令。

 ↑ x = 1; var localy = y; ↓ 

值得注意的提及

同样重要的是要注意:

  • x86硬件在写入时具有易失性语义。
  • 微软的CLI实现(以及可疑的Mono)在写入时具有易失性语义。
  • ECMA规范在写入时没有 volatile语义。

你是对的。 在Joseph Albahari线索书/文章中更详细说明。

MSDN文档指出使用volatile关键字可确保始终在字段中显示最新值。 这是不正确的,因为正如我们所见,可以重新排序读后跟读。

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

我(作为程序员)是否需要阻止使用此关键字 – 因为这种奇怪的行为?

只有在知道了这种习惯行为后才能使用它。 它不应该用作Magic关键字来在multithreading环境中始终检索最新值。

IMO,应避免使用volatile关键字,因为很难找到可能的错误。

Joseph Albahari提出的另一个观点是,流程架构会对易失性产生负面影响,特别是AMD会导致值被交换。

由于您可能不知道应用程序将在生产中运行哪种系统类型,因此最好始终避免使用volatile关键字。

稍微偏离主题你应该总是避免ReaderWriterLock类,因为在多处理器系统上负载很重,这可以允许多个写锁, 请看这里同时进行,这将导致应用程序挂起,这将是根本原因非常困难