![]() |
1
2
每个原子物体(单独地)都有其修改的总顺序,即所谓的 修改令 ,所有线程必须在全球范围内达成一致。 原子对象上的每个副作用(即存储)都构成了此修改顺序中的一个条目。 原子对象上的每个值计算(即加载) 接受其价值 除了一些一致性规则外,这种修改顺序中的一个副作用是未指明的。 这些一致性规则在 cppreference page 您链接了:
请注意,上面使用的是非正式语言,如“ A的值来自 “,正式应该是” A的值计算从 “.正式规范见 [intro.races]/14 以及以下段落。 " 线程B中的负载读取线程a中存储器写入的值 “也是一种不太正式的表达方式” 线程B中的值计算按照原子对象的修改顺序从线程A中的副作用中获取其值 ". 你的报价是说 与同步 关系仅发生在对原子对象产生副作用的特定评估中,从该评估中计算值 接受其价值 . 这特别意味着,如果修改顺序中有两个副作用,都将相同的值存储到原子中,那么考虑到上述一致性规则,原子上的值计算可能会 接受它的价值 来自两者中的任何一个。虽然通过查看负载产生的值无法区分这两种情况,但它仍然会影响 与同步 关系以及内存同步保证您将获得什么。 正如@PeterCodes在这个答案下的评论中指出的那样,发布/获取的规则意味着 与同步 引用cppreference也是不完整的。事实上 与同步 这种关系不仅与导致价值计算产生副作用的评估建立关系,而且与价值计算产生的副作用建立关系。 相反,如果值计算从所谓的 发布序列由以下人员领导 所考虑的释放操作是由从所考虑的发布操作开始的释放操作组成的修改顺序的连续子序列。 这意味着,如果该值是从修改顺序中的某个释放操作中获取的,并且在它之前还有另一个释放操作,那么通常获取操作也是 与同步 之前的发布操作。 但由于一些不常见的CPU内存型号,这不适用于 全部的 之前的发布操作。当前的定义 释放顺序 可以在以下网址找到 [intro.races]/5 并且有(正在进行的?)关于它的讨论。具体来说,当前的定义只包含释放操作,这些操作也是读-修改-写操作。 天真的人可能会期望 与同步 与修改顺序中所有先前版本存储的应用关系,因此这可能会导致一些令人惊讶的结果。 例如,考虑以下场景:
然后线程3可以安全地检索数据B,但由于非RMW修改不是发布序列的一部分,因此不会与线程1进行任何同步,访问数据A将是一场数据竞赛。 |
![]() |
Bogey · C#共享内存-CPU缓存风险(非易失性读取)? 6 年前 |
![]() |
filo · 如何在x86上使用gcc强制执行内存排序 7 年前 |
![]() |
maxim1 · Java中的易失阵列、内存障碍和可见性 8 年前 |
![]() |
paddy · 双原子自旋锁的最小限制存储器排序 9 年前 |