重新排序原子读取

时间:2016-01-01 08:15:46

标签: c++ multithreading concurrency atomic

我正在研究一种多线程算法,该算法读取两个共享原子变量:

std::atomic<int> a(10);
std::atomic<int> b(20);

void func(int key) {
   int b_local = b;
   int a_local = a;
   /* Some Operations on a & b*/
}

算法的不变量是在阅读b之前应该读取a

问题是,编译器(比方说GCC)可以重新排序指令,以便在a之前读取b吗?使用显式内存栅栏可以实现这一点,但我想要理解的是,可以重新排序两个原子负载。

此外,在通过Herb Sutter的谈话(http://herbsutter.com/2013/02/11/atomic-weapons-the-c-memory-model-and-modern-hardware/)获取Acquire / Release语义后,我理解顺序一致的系统确保了获取(如加载)和释放(如存储)之间的排序。如何在两次获取之间进行排序(如两次加载)?

编辑:添加有关代码的更多信息: 考虑两个线程T1和amp; T2执行:

T1:读取b的值,休眠

T2:更改a的值,返回

T1:唤醒并读取新值a(新值)

现在,请考虑重新排序的情况:

int a_local =a; int b_local = b;

T1:读取a的值,休眠

T2:更改a的值,返回

T1:不知道有关a的价值变化的任何事情。

问题是“像GCC这样的编译器可以重新排序两个原子载荷吗?

4 个答案:

答案 0 :(得分:0)

是的,它们可以重新排序,因为一个订单与另一个订单没有区别,并且您没有任何约束来强制任何特定订单。这些代码行之间只有一种关系:int b_local = b;int a_local = a;之前排序,但由于您的代码中只有一个线程,而且两行是独立的,因此它完全无关紧要对于第3行代码(无论该行可能是什么),首先完成哪一行,因此编译器可能会毫无疑问地对其进行重新排序。

因此,如果您需要强制执行某些特定订单:

  1. 2个以上执行线程

  2. 在这些线程中的两个操作之间建立发生之前的关系。

答案 1 :(得分:0)

memory_order_acquire

Description

  

在此加载之前,当前线程中的内存访问不能重新排序。

作为加载b的默认内存顺序是memory_order_seq_cst,这是最强的,在a阅读之前,无法重新排序b

即使是较弱的记忆订单,如下面的代码所示,提供相同的保证:

int b_local = b.load(std::memory_order_acquire);
int a_local = a.load(std::memory_order_relaxed);

答案 2 :(得分:-1)

以下是调用作业时__atomic_base正在执行的操作:

  operator __pointer_type() const noexcept
  { return load(); }

  _GLIBCXX_ALWAYS_INLINE __pointer_type
  load(memory_order __m = memory_order_seq_cst) const noexcept
  {
    memory_order __b = __m & __memory_order_mask;
    __glibcxx_assert(__b != memory_order_release);
    __glibcxx_assert(__b != memory_order_acq_rel);

    return __atomic_load_n(&_M_p, __m);
  }

根据内置类的GCC文档,如__atomic_load_n:

https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html

"An atomic operation can both constrain code motion and be mapped to hardware instructions for synchronization between threads (e.g., a fence). To which extent this happens is controlled by the memory orders, which are listed here in approximately ascending order of strength. The description of each memory order is only meant to roughly illustrate the effects and is not a specification; see the C++11 memory model for precise semantics.


__ATOMIC_RELAXED
    Implies no inter-thread ordering constraints.
__ATOMIC_CONSUME
    This is currently implemented using the stronger __ATOMIC_ACQUIRE memory order because of a deficiency in C++11's semantics for memory_order_consume.
__ATOMIC_ACQUIRE
    Creates an inter-thread happens-before constraint from the release (or stronger) semantic store to this acquire load. Can prevent hoisting of code to before the operation.
__ATOMIC_RELEASE
    Creates an inter-thread happens-before constraint to acquire (or stronger) semantic loads that read from this release store. Can prevent sinking of code to after the operation.
__ATOMIC_ACQ_REL
    Combines the effects of both __ATOMIC_ACQUIRE and __ATOMIC_RELEASE.
__ATOMIC_SEQ_CST
    Enforces total ordering with all other __ATOMIC_SEQ_CST operations. "

所以,如果我正确地读这个,它确实会限制代码运动&#34;,我读到这意味着阻止重新排序。但我可能误解了文档。

答案 3 :(得分:-1)

是的,我认为除了几项优化之外,它还可以重新排序。 请检查以下资源: Atomic vs. Non-Atomic Operations

如果您仍然担心此问题,请尝试使用互斥锁,这肯定会阻止内存重新排序。