volatile是否会阻止引入读取或写入?

在C#中, volatile关键字分别确保读取和写入具有获取和释放语义。 但是,是否有关于引入读取或写入的内容?

例如:

 volatile Thing something; volatile int aNumber; void Method() { // Are these lines... var local = something; if (local != null) local.DoThings(); // ...guaranteed not to be transformed into these by compiler, jitter or processor? if (something != null) something.DoThings(); // <-- Second read! // Are these lines... if (aNumber == 0) aNumber = 1; // ...guaranteed not to be transformed into these by compiler, jitter or processor? var temp = aNumber; if (temp == 0) temp = 1; aNumber = temp; // <-- An out-of-thin-air write! } 

以下是C#规范1对执行顺序的评价 :

执行C#程序,使得每个执行线程的副作用在关键执行点处得以保留。 副作用定义为易失性字段的读取或写入…

执行环境可以自由更改C#程序的执行顺序,但受以下限制:

关于易失性读写,保留副作用的顺序……

我当然会考虑引入新的副作用来改变副作用的顺序,但是这里没有明确说明。


答案中的链接是列为草稿的C#6规范。 C#5规范不是草稿,但不能在线获取,仅作为下载 。 在本节中我可以看到相同的措辞。

这个措辞来自C#规范:

关于易失性读写,保留副作用的顺序……

可能被解释为暗示不允许对volatile变量进行读写引入,但它实际上是模糊的,它取决于“排序”的含义。 如果它指的是现有访问的相对排序,那么引入新的读取或写入不会改变它,因此它不会违反规范的这一部分。 如果它是指程序顺序中所有存储器访问的确切位置,则引入新访问将违反规范。

本文说可能会引入非易失volatile变量的读取,但没有明确说明volatile变量是否允许这样做。

本Q / A讨论了如何防止读取介绍(但没有讨论写入介绍)。

在本文的评论中,两位Microsoft员工(至少在撰写评论时)明确声明不允许对volatile变量进行读写介绍。

斯蒂芬图布

“读取介绍”是可以引入内存重新排序的一种机制。

伊戈尔奥斯特罗夫斯基

在C#规范的其他地方,易失性读取被定义为“副作用”。 因此,重复读取m_paused将等同于添加另一个副作用,这是不允许的。

我认为我们可以从这些评论中得出结论,在C#中引入无效的副作用,任何类型的副作用都不允许在代码中的任何地方。

CLI标准的相关引用在第I.12.6.7节中说明了以下内容:

将CIL转换为本机代码的优化编译器不应删除任何易失性操作,也不应将多个易失性操作合并为单个操作。

据我所知,CLI没有明确谈论引入新的副作用。

我想知道你是否误解了volatile意味着什么。 易失性可以与可以作为primefaces动作读取或写入的类型一起使用。

没有获取/释放 ,只是编译时和运行时重新排序的障碍,以提供无锁的获取/释放语义( https://preshing.com/20120913/acquire-and-release-semantics/ )。 在非x86上,这可能需要asm中的屏障指令,但不能锁定。

volatile表示某个字段可能被其他线程修改,这就是为什么读/写需要被视为primefaces而不是优化的原因。


你的问题有点含糊不清。

1 /如果您的意思是,编译器将转换:

 var local = something; if (local != null) local.DoThings(); 

成:

 if (something != null) something.DoThings(); 

然后答案是否定的。

2 /如果你的意思是,“ DoThings() ”将在同一个对象上被调用两次:

 var local = something; if (local != null) local.DoThings(); if (something != null) something.DoThings(); 

那么答案肯定是肯定的,除非另一个线程在调用第二个“ DoThings() ”之前改变了“ something ”的值。 如果是这种情况那么它可能会给你一个运行时错误 – 如果在评估“ if ”条件之后并且在调用“ DoThings ”之前,另一个线程将“ something ”设置为null然后你将得到一个运行时错误。 我假设这就是为什么你有“ var local = something; ”。

3 /如果你的意思是以下两个原因:

 if (something != null) something.DoThings(); 

然后是,一个读取条件,第二个读取它调用DoThings() (假设something不为null)。 如果它没有标记为volatile那么编译器可以通过单次读取来管理它。

无论如何,函数“ DoThings() ”的实现需要知道它可以由多个线程调用,因此需要考虑合并锁和它自己的volatile成员的组合。