了解 CLR 2.0 内存模型
Joe Duffy 给出了描述 CLR 2.0+ 内存模型的 6 条规则 (这是实际的实现,而不是任何 ECMA 标准)我正在写下我试图解决这个问题的尝试,主要是作为一种橡皮鸭的方式,但如果我在逻辑上犯了错误,至少这里有人能够抓住在它给我带来悲伤之前。
- 规则 1:负载之间的数据依赖性 并且商店从未受到侵犯。
- 规则 2:所有存储都有发布语义, 即之后没有负载或存储可以移动 一。
- 规则 3:所有易失负载均 获取,即不能加载或存储 移动到一个之前。
- 规则 4:无负载且 商店可能会跨越完整的障碍 (例如Thread.MemoryBarrier、锁 获取、联锁、交换、 Interlocked.CompareExchange 等)。
- 规则 5:加载并存储到堆 可能永远不会被介绍。
- 规则 6: 加载和存储只能被删除 当合并相邻负载时并且 商店往返于同一地点。
我正在尝试理解这些规则。
x = y
y = 0 // Cannot move before the previous line according to Rule 1.
x = y
z = 0
// equates to this sequence of loads and stores before possible re-ordering
load y
store x
load 0
store z
从这个角度来看,负载 0 似乎可以移动到负载 y 之前,但存储可能根本无法重新排序。因此,如果一个线程看到 z == 0,那么它也会看到 x == y。
如果 y 是不稳定的,则负载 0 不能在负载 y 之前移动,否则可以。易失性商店似乎没有任何特殊属性,没有商店可以相互重新排序(这是一个非常有力的保证!)
完整的障碍就像沙子里的一条线,装载和商店不能移动超过。
不知道规则5是什么意思。
我猜规则 6 的意思是,如果您这样做:
x = y
x = z
那么 CLR 可能会同时删除对 y 的加载和对 x 的第一个存储。
x = y
z = y
// equates to this sequence of loads and stores before possible re-ordering
load y
store x
load y
store z
// could be re-ordered like this
load y
load y
store x
store z
// rule 6 applied means this is possible?
load y
store x // but don't pop y from stack (or first duplicate item on top of stack)
store z
如果 y 不稳定怎么办?我在规则中没有看到任何禁止执行上述优化的内容。这并不违反双重检查锁定,因为两个相同条件之间的 lock() 会阻止负载移动到相邻位置,并且根据规则 6,这是唯一可以消除负载的时间。
所以我想我理解除了规则 5 之外的所有内容。任何人都想启发我(或纠正我或为上述任何内容添加一些内容?)
Joe Duffy, gives 6 rules that describe the CLR 2.0+ memory model (it's actual implementation, not any ECMA standard) I'm writing down my attempt at figuring this out, mostly as a way of rubber ducking, but if I make a mistake in my logic, at least someone here will be able to catch it before it causes me grief.
- Rule 1: Data dependence among loads
and stores is never violated. - Rule 2: All stores have release semantics,
i.e. no load or store may move after
one. - Rule 3: All volatile loads are
acquire, i.e. no load or store may
move before one. - Rule 4: No loads and
stores may ever cross a full-barrier
(e.g. Thread.MemoryBarrier, lock
acquire, Interlocked.Exchange,
Interlocked.CompareExchange, etc.). - Rule 5: Loads and stores to the heap
may never be introduced. - Rule 6:
Loads and stores may only be deleted
when coalescing adjacent loads and
stores from/to the same location.
I'm attempting to understand these rules.
x = y
y = 0 // Cannot move before the previous line according to Rule 1.
x = y
z = 0
// equates to this sequence of loads and stores before possible re-ordering
load y
store x
load 0
store z
Looking at this, it appears that the load 0 can be moved up to before load y, but the stores may not be re-ordered at all. Therefore, if a thread sees z == 0, then it also will see x == y.
If y was volatile, then load 0 could not move before load y, otherwise it may. Volatile stores don't seem to have any special properties, no stores can be re-ordered with respect to each other (which is a very strong guarantee!)
Full barriers are like a line in the sand which loads and stores can not be moved over.
No idea what rule 5 means.
I guess rule 6 means if you do:
x = y
x = z
Then it is possible for the CLR to delete both the load to y and the first store to x.
x = y
z = y
// equates to this sequence of loads and stores before possible re-ordering
load y
store x
load y
store z
// could be re-ordered like this
load y
load y
store x
store z
// rule 6 applied means this is possible?
load y
store x // but don't pop y from stack (or first duplicate item on top of stack)
store z
What if y was volatile? I don't see anything in the rules that prohibits the above optimization from being carried out. This does not violate double-checked locking, because the lock() between the two identical conditions prevents the loads from being moved into adjacent positions, and according to rule 6, that's the only time they can be eliminated.
So I think I understand all but rule 5, here. Anyone want to enlighten me (or correct me or add something to any of the above?)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
Joe Duffy 在 Windows 上的并发编程 的 pp517-18 上讨论了规则 5:
我在博客中介绍了一个重要的地方:提高的标准模式一个事件。
为了防止在单独线程上删除事件处理程序时出现问题,我们读取 MyEvent 的当前值,并且仅在该委托非空时调用事件处理程序。
如果可以引入从堆读取,编译器/JIT 可能会决定再次读取
MyEvent
,而不是使用本地,这会引入竞争条件。Joe Duffy discusses Rule 5 on pp517-18 of Concurrent Programming on Windows:
I blogged about one important place where this matters: the standard pattern for raising an event.
In order to prevent problems with removing an event handler on a separate thread, we read the current value of
MyEvent
and only invoke the event handlers if that delegate is non-null.If reads from the heap could be introduced, the compiler/JIT might decide that it could be better to read
MyEvent
again, rather than using the local, which would introduce a race condition.