挥发性违反其主要工作?
根据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:一种内存屏障,在屏障后不允许其他读写操作。
我将尝试使用箭头符号解释表格。 ↓箭头将标记易失性读数,↑箭头将标记易失性写入。 没有指令可以通过箭头移动。 把箭头想象成推开一切。
在下面的分析中我将使用变量; x
和y
。 我还假设它们被标记为volatile
。
情况1
注意在读取x
之后箭头的放置如何防止读取y
向上移动。 另请注意,在这种情况下, y
的波动性无关紧要。
var localx = x; ↓ var localy = y; ↓
案例#2
注意在读取x
之后箭头的放置如何防止写入y
向上移动。 另请注意,在这种情况下, x
或y
的任何一个的波动性,但不是两者的波动性都可以省略。
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类,因为在多处理器系统上负载很重,这可以允许多个写锁, 请看这里同时进行,这将导致应用程序挂起,这将是根本原因非常困难