问题标签 [relaxed-atomics]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
c++ - 放松的原子规则有什么(轻微的)区别?
在看到 Herb Sutters关于“原子武器”的精彩演讲后,我对“轻松原子”示例感到有些困惑。
我认为C++ 内存模型中的原子(SC-DRF = Sequentially Consistent for Data Race Free)在加载/读取时执行“获取”。
我知道对于负载 [和商店],默认值是std::memory_order_seq_cst
,因此两者是相同的:
到目前为止一切顺利,没有涉及轻松原子(在听完演讲后,我永远不会使用轻松的原子。永远。承诺。但是当有人问我时,我可能不得不解释......)。
但是为什么当我使用时它是“宽松”的语义
既然负载是获取而不是释放,为什么这与(1)
and不同(2)
?这里真正放松的是什么?
我唯一能想到的就是我误解了load的意思是acquire。如果这是真的,并且默认值seq_cst
意味着两者,那是否意味着一个完整的围栏 - 没有什么可以传递该指令,也不能传递?我必须误解了那部分。
[并且对称地存储和释放]。
c++ - 为什么 memory_order_relaxed 在 x86 上使用原子(锁定前缀)指令?
在 Visual C++ 2013 上,当我编译以下代码时
我在 x86 上返回以下程序集:
同样在 x64 上:
我只是不明白:为什么变量的轻松增量int
需要lock
前缀?
这是有原因的,还是他们根本不包括删除它的优化?
* 我用/O2
with/NoDefaultLib
来修剪它并去掉不必要的 C 运行时代码,但这与问题无关。
c++ - 什么时候可以做/使用具有未指定行为的东西?
在 C++ 中,有些东西出现在定义明确和未定义之间。具体来说,它们被称为实现定义和未指定。现在,我对未指定的东西很感兴趣。
什么时候可以使用这些功能,什么时候应该避免使用?有没有很好的例子说明未指定的行为是正确代码的一部分?编写软件时,什么时候是最好的选择?
Matt McNabb 提供的定义:
未定义 - 任何事情都可能发生
实现定义 - 有限数量的结果是可能的,编译器的文档必须说明会发生什么
未指定- 有限数量的结果是可能的 - 通常标准描述一组可能的结果
定义明确 - 以上都不是
格式良好的程序 - 编译时没有错误的程序(可能表现出未定义的行为)
后续问题:
宽松的原子算作未指定的还是明确定义的?
标记为从不同角度讨论相同想法的问题的副本。标记为相同的问题讨论了未指定行为的定义,而这里的问题是关于如何以及何时使用它。
c++ - 无锁堆栈 - 这是 c++11 宽松原子的正确用法吗?可以证明吗?
我为需要跨线程同步的非常简单的数据块编写了一个容器。我想要最好的表现。我不想使用锁。
我想使用“放松”的原子。部分是为了那一点额外的魅力,部分是为了真正理解它们。
我一直在做这方面的工作,而且我正处于这段代码通过了我对其进行的所有测试的地步。不过,这还不是“证据”,所以我想知道我是否缺少任何东西,或者我可以通过其他任何方式来测试它吗?
这是我的前提:
- 唯一重要的是正确推送和弹出节点,并且堆栈永远不会失效。
- 我相信内存中的操作顺序只在一个地方很重要:
- 在 compare_exchange 操作本身之间。即使使用宽松的原子,也可以保证这一点。
- “ABA”问题通过向指针添加标识号来解决。在 32 位系统上,这需要双字 compare_exchange,而在 64 位系统上,指针的未使用的 16 位用 id 号填充。
- 因此:堆栈将始终处于有效状态。 (正确的?)
这就是我的想法。“通常”,我们对正在阅读的代码进行推理的方式是查看它的编写顺序。内存可以“乱序”读取或写入,但不能以使程序正确性无效的方式。
这在多线程环境中发生了变化。这就是内存栅栏的用途——这样我们仍然可以查看代码并能够推断它是如何工作的。
所以,如果这里一切都乱了套,那我用宽松的原子做什么呢?是不是有点太远了?
我不这么认为,但这就是我在这里寻求帮助的原因。
compare_exchange 操作本身保证了彼此的顺序不变性。
对原子进行读取或写入的唯一其他时间是在 compare_exchange 之前获取头部的初始值。它被设置为变量初始化的一部分。据我所知,此操作是否带回“正确”值是无关紧要的。
当前代码:
与其他问题相比,这个问题有什么不同?轻松的原子。他们对这个问题有很大的影响。
所以你怎么看?有什么我想念的吗?
c++ - atomic_thread_fence(memory_order_seq_cst) 是否具有完整内存屏障的语义?
完整/通用内存屏障是指在屏障之前指定的所有 LOAD 和 STORE 操作相对于系统的其他组件似乎都发生在屏障之后指定的所有 LOAD 和 STORE 操作之前。
根据cppreference,memory_order_seq_cst
等于memory_order_acq_rel
加上对所有如此标记的操作的单个总修改顺序。但据我所知,C++11 中的获取和释放栅栏都不强制执行#StoreLoad(存储后加载)排序。释放栅栏要求之前的读/写不能与任何后续写入重新排序;获取栅栏要求不能对任何先前的读取重新排序后续读取/写入。如果我错了,请纠正我;)
举个例子,
优化编译器是否允许将指令 (3) 重新排序到 (1) 之前,使其有效如下所示:
如果这是一个有效的转换,那么它证明atomic_thread_fence(memory_order_seq_cst)
不一定包含完整屏障所具有的语义。
c++ - memory_order_relaxed 如何在智能指针中增加原子引用计数?
考虑以下取自 Herb Sutter 关于原子的演讲的代码片段:
smart_ptr 类包含一个名为 control_block_ptr 的 pimpl 对象,其中包含引用计数refs。
Herb Sutter 说线程 A 中refs的增量可以使用 memory_order_relaxed 因为“没有人根据动作做任何事情”。现在,据我了解 memory_order_relaxed,如果refs在某个时候等于 N,并且两个线程 A 和 B 执行以下代码:
那么可能会发生两个线程都看到refs的值为N 并且都将 N+1 写回它的情况。这显然行不通,应该像使用析构函数一样使用 memory_order_acq_rel。我哪里错了?
EDIT1:考虑以下代码。
在调用 fetch_add 之前,线程 2 观察到的refs的值是多少?可以是 N 还是 N+1?调用 fetch_add 后线程 2 观察到的 refs 的值是多少?必须至少为 N+2 吗?
[谈话网址:C++ & Beyond 2012 - http://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Herb-Sutter-atomic-Weapons-2-of-2 (@ 1: 20:00)]
c++ - C++11 是否保证释放栅栏和消耗操作之间的内存排序?
考虑以下代码:
C++ 是否对线程 a 中的栅栏与线程 b 中的消费操作的交互做出任何保证?
我知道在这个例子中我可以用 store-release 替换栅栏 + atomic store 并让它工作。但我的问题是关于使用围栏的这种特殊情况。
阅读标准文本,我可以找到关于释放栅栏与获取栅栏的交互以及释放栅栏与获取操作的交互的条款,但没有关于释放栅栏和消费操作的交互的条款。
我认为,用获取替换消耗将使代码符合标准。但据我了解处理器实现的内存排序约束,我应该只需要线程 b 中较弱的“消耗”排序,因为内存屏障强制线程 a 中的所有存储在存储到指针之前可见,并且读取有效负载取决于从指针读取。
标准一致吗?
c++ - 了解 memory_order_relaxed
我试图了解 memory_order_relaxed 的细节。我指的是这个链接:CPP 参考。
问题 1:在上面的代码中,从技术上讲,fun2 是否有可能处于无限循环中,即使设置 ptr 的线程已经完成运行,它也会将 ptr 的值视为 nullptr?
如果假设,我将上面的代码更改为以下代码:
相关问题:在上面的代码中 fun2 是否有可能将 atomic i 的值视为 1 或者确定它会看到值 2?
c++ - 具有 memory_order_relaxed 的存储是否有可能永远不会到达其他线程?
假设我有一个线程Aatomic_int x = 0;
使用x.store(1, std::memory_order_relaxed);
. 如果没有任何其他同步方法,其他线程需要多长时间才能看到这个,使用x.load(std::memory_order_relaxed);
?x
考虑到标准给出的 C/C++ 内存模型的当前定义,写入的值是否可能完全保持在线程本地?
我手头的实际情况是线程B经常读取 anatomic_bool
以检查它是否必须退出;在某个时候,另一个线程对该布尔值写入true,然后在线程 B 上调用 join()。显然,我不介意在线程 B 甚至可以看到 atomic_bool 已设置之前调用 join(),我也不介意线程何时在我调用 join() 之前,B 已经看到了更改并退出了执行。但我想知道:memory_order_relaxed
在双方都使用时,是否可以调用 join() 并“永远”阻塞,因为更改永远不会传播到线程 B?
编辑
我联系了 Mark Batty(数学验证并随后修复 C++ 内存模型要求的大脑)。最初是关于其他的(结果是 cppmem 和他的论文中的一个已知错误;所以幸运的是我没有完全自欺欺人,也借此机会向他询问了这个问题;他的回答是:
问:理论上这样的存储[memory_order_relaxed without (any following) release operation]是否永远不会到达其他线程?
马克:理论上,是的,但我认为没有观察到这一点。
问:换句话说,如果您希望另一个线程看到它,除非您将它们与一些释放操作(并在另一个线程上获取)结合起来,否则轻松存储是否毫无意义?
马克:几乎所有的用例都使用发布和获取,是的。
c++ - C++ 标准:可以将宽松的原子存储提升到互斥锁之上吗?
标准中是否有任何措辞保证不会将宽松的原子存储提升到互斥锁的锁定之上?如果没有,是否有任何措辞明确表示编译器或 CPU 这样做是符合犹太教规的?
例如,使用下面的程序(它可能使用 acq/rel 来foo_has_been_set
避免锁,和/或使foo
自己成为原子的。这样写是为了说明这个问题。)
如果另一个线程同时调用,是否有可能CheckFoo
在上述程序中崩溃SetFoo
,或者是否有一些保证不能将存储提升到编译器和 CPUfoo_has_been_set
的调用之上?mu.lock
这与一个较旧的问题有关,但我并不是 100% 清楚那里的答案适用于此。特别是,该问题答案中的反例可能适用于对 的两个并发调用SetFoo
,但我对编译器知道有一个调用SetFoo
和一个调用的情况感兴趣CheckFoo
。能保证安全吗?
我正在寻找标准中的特定引用。