挥发性违反其主要工作?

时间:2012-05-17 07:34:48

标签: c# .net multithreading thread-safety volatile

根据MSDN

  

volatile关键字表示字段可能被修改   多个线程同时执行。是的领域   声明的volatile不受编译器优化的限制   假设由单个线程访问。这确保了最多   字段中始终存在最新值。

请注意最后一句:

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

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

read它可以改变指令的顺序:

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

这意味着John将值设置为易失性字段,稍后 Paul想要读取该字段, Paul正在获得值!

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

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

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

3 个答案:

答案 0 :(得分:18)

你是对的。约瑟夫·阿尔巴哈里(Joseph Albahari)更加详细地阐述了book/article

  
    

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

  

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

  
    

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

  

只有在了解了这种疲惫的行为后才能使用它。它不应该用作Magic关键字来在多线程环境中一直检索最新值。

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

答案 1 :(得分:7)

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硬件在写入时具有易失性语义。
  • Microsoft的CLI实现(以及可疑的Mono)在写入时具有易失性语义。
  • ECMA规范在写入时具有易失性语义。

答案 2 :(得分:4)

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

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

稍微偏离主题你应该总是避免ReaderWriterLock类,因为在多处理器系统上负载很重,这可能允许同时采用多个写锁please see here,这将导致应用程序挂起,这将导致根本原因非常困难< / p>