来自MSDN,Volatile.Read()
读取字段的值。在需要它的系统上,插入一个阻止处理器重新排序内存操作的内存屏障,如下所示:如果在代码中的此方法之后出现读或写,则处理器不能在之前将其移动到之前。
和Volatile.Write()
将值写入字段。在需要的系统上,插入一个内存屏障,以防止处理器按以下方式重新排序内存操作:如果在代码中在此方法之前出现读或写,则处理器不能在之后将其移动到之后。
我想我可以理解Volatile.Read()和Volatile.Write()的使用场景,并看到了许多例子来解释为什么这两种方法有助于确保程序的正确性。
但我仍然想知道,这些规则背后的逻辑是什么?
以Volatile.Read()为例,为什么它需要在之后的操作--不能在之前移动,而不需要在之前从操作中获取任何东西?
以及它与Volatile.Write()相反的原因
谢谢!
发布于 2017-01-11 04:33:36
围绕易失性读取和易失性写入的保证确保,如果一个线程使用一个易失性写入来指示某些事情已经完成,而另一个线程使用一个易失性读取来注意到某个事情已经完成,那么第二个线程将看到该事情的全部效果。
例如,让我们假设Thread1初始化对象A,而不是对表示已经完成的flag进行易失性写入。初始化对象A字段所涉及的所有内存操作都发生在代码中的标志设置之前。保证这些“在易失性写入之后不能移动”到flag,因此在内存中设置标志时,整个初始化对象都在其他线程可以看到的内存中。
现在让我们说,Thread2正在等待该对象。它有一个易失性的读取,可以看到flag被设置,然后读取A的字段,并根据它所读的内容做出决定。这些读取操作发生在代码中的易失性读取之后,而易失性读取保证它们在内存中的易失性读取之后发生,从而保证Thread2能够看到对象A的完全初始化字段,而不是它之前存在的任何内容。
因此:Thread1确实在flag的易失性写入之前将其写入内存,显然,在Thread2能够对其进行易失性读取之前,必须先将其释放到内存中,然后在Thread2中进行以下读取,以便看到正确初始化的对象。
这就是为什么写入不能在易失性写入之后延迟,并且在易失性读取之前不能向上移动读取。反之亦然?
那么,让我们说,Thread2在看到A已经初始化之后,会做一些工作,并将其写入Thread1用来决定如何初始化A的内存中。这些写入保证在Thread2看到A完成之前不会在内存中发生,而Thread1对这些位置的读取保证在内存中设置flag之前发生,因此保证Thread2的写入不会干扰初始化工作。
发布于 2017-01-13 13:42:13
这些规则背后的逻辑称为内存模型。
在.NET中,我们有相当弱的内存模型(请参阅ECMA-335),这意味着编译器、jit和cpu可以进行许多优化(只要它们保持单线程语义和易失性语义),而且就优化的可能性而言,这是非常棒的。
它允许编译器/jit/cpu进行任何优化,只要它们满足以下条件:
符合CLI的实现可以自由地使用任何技术来执行程序,这种技术可以保证线程生成的副作用和异常按照CIL指定的顺序可见。为此目的,只有易失性操作(包括易失性读取)才构成明显的副作用。(请注意,虽然只有易失性操作构成可见的副作用,但易失性操作也会影响非易失性引用的可见性。)
这意味着,除非使用隐式或显式易失性操作,否则所有代码都假定为单线程。
例如,
获取锁( System.Threading.Monitor.Enter或输入同步方法)将隐式执行易失性读取操作,释放锁( System.Threading.Monitor.Exit或离开同步方法)将隐式执行易失性写入操作。
这意味着不可能在上面移动任何操作(从lock语句)(隐式Volatile.Read防止这一点),也不可能将它们移到锁下面(隐式Volatile.Write防止这一点)。因此,它们就在lock语句中,但是仍然有可能在这个lock语句中对它们进行重新排序或优化。
发布于 2020-12-20 13:51:10
但我仍然想知道,这些规则背后的逻辑是什么?
以Volatile.Read()为例,为什么它需要在它之前不能移动之后的操作,而不需要它之前的操作中的任何东西?
让我们看看根据volatile语义,什么是可能的。
RULES
Volatile.Read ┇ Volatile.Write
⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩ ┇ ⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩
** later cannot move here ** ┇ x = writeSomething
°MemoryBarier° ┇ °MemoryBarier°
x = readSomething ┇ ** sooner cannot move here **
〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰
EXECUTION ORDER (2 OPTIONS)
W-R R-W
⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩ ⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩
Write (sooner) ┇ Read (sooner)
Read (later) ┇ Write (later)
〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰〰
REORDER
W-R R-W
⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩ ⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩⬩
** later cannot move here ** ┇ Not possible
°MemoryBarier° ¦ (later) ┇
x = readSomething ¦ (later) ┇
x = writeSomething ¦ (sooner) ┇
°MemoryBarier° ¦ (sooner) ┇
** sooner cannot move here ** ┇您可以看到,volatile、read和write的规则可能有点违背直觉。
看过“之前”的可能性表
Read | Write | Volatile.Write | Volatile.Read
Volatile.Read | Volatile.Read | Volatile.Read | Volatile.Read
---------------|-----------------|------------------|-------------------
No harm when | Can be | Can be reordered.| Cannot be reordered.
reordered. | reordered. | Can be a harm. |
One should use | Missing MT tech.| |
some MT | Can be a harm. | |
technics. | | | 因此,在指令之前,任何合理的指令都已受volatility规则的管辖。
以及为什么它与Volatile.Write()相反?
每一个都是相辅相成的。因此,我们可以根据需要以一种read和write的方式进行volatile。
https://stackoverflow.com/questions/41582207
复制相似问题