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成员的组合。