【发布时间】:2017-03-31 15:40:38
【问题描述】:
考虑一个
std::atomic<int> x(0);
假设我有一个执行以下操作的函数:
int x_old = x.fetch_add(1,std::memory_order_acq_rel);
基于description for acquire release memory ordering:
memory_order_relaxed 宽松操作:没有同步或排序约束,此操作只需要原子性(请参阅下面的宽松排序)
memory_order_consume 使用此内存顺序的加载操作对受影响的内存位置执行消耗操作:当前线程中依赖于当前加载的值的读取或写入不能在此加载之前重新排序。在释放相同原子变量的其他线程中写入数据相关变量在当前线程中是可见的。在大多数平台上,这只会影响编译器优化(请参阅下面的发布-使用顺序)
memory_order_acquire 使用此内存顺序的加载操作会在受影响的内存位置上执行获取操作:在此加载之前,当前线程中的任何读取或写入都不能重新排序。释放相同原子变量的其他线程中的所有写入在当前线程中都是可见的(请参阅下面的 Release-Acquire 顺序)
memory_order_release 具有此内存顺序的存储操作执行释放操作:当前线程中的任何读取或写入都不能在此存储之后重新排序。当前线程中的所有写入在获取相同原子变量的其他线程中都是可见的(请参阅下面的 Release-Acquire 排序),并且将依赖项携带到原子变量中的写入在使用相同原子的其他线程中变得可见(请参阅 Release-Consume在下面订购)。
memory_order_acq_rel 具有这种内存顺序的读-修改-写操作既是获取操作又是释放操作。当前线程中的内存读取或写入不能在此存储之前或之后重新排序。释放相同原子变量的其他线程中的所有写入在修改之前都是可见的,并且修改在获取相同原子变量的其他线程中是可见的。
memory_order_seq_cst 任何具有此内存顺序的操作既是获取操作又是释放操作,另外存在一个总顺序,其中所有线程以相同顺序观察所有修改(请参阅顺序一致的顺序下面)
是否有可能 2 个不同的线程接收相同的 x_old 值 0?或者它们是否保证以 x_old 仅对其中一个为 0,而对另一个为 1 的方式执行。
如果x_old 对它们都可能为0,那么将内存顺序更改为std::memory_order_seq_cst 是否保证x_old 的唯一性?
【问题讨论】:
-
内存排序不相关。两个线程不可能获得相同的值(假设表达式在每个线程中只到达一次)。
-
为什么内存排序不重要?难道两个线程都不能在 acq_rel 下获得相同的 x_old,因为没有保证读-修改-写操作的读-读顺序?
-
排序在这里根本不相关。从 fetch_add 返回的前两个值将始终为:0 和 1,但不能保证哪个线程获得哪个值。无论您选择哪种 memory_order 都是如此。
-
在这种情况下,fetch_add 操作上的 seq_cst 和 acq_rel memory_ordering 究竟有什么区别?
-
使用 seq_cst 的操作还可以防止跨它的其他获取和释放操作的重新排序。
标签: c++ atomic memory-model stdatomic