无锁编程:重新排序和存储顺序语义 [英] Lock-free programming: reordering and memory order semantics

查看:71
本文介绍了无锁编程:重新排序和存储顺序语义的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在无锁编程中站稳脚跟.阅读了有关内存排序语义的不同解释后,我想弄清楚可能发生什么重新排序.据我了解,指令可能会由编译器(由于在编译程序时进行了优化)和CPU(在运行时?)重新排序.

I am trying to find my feet in lock-free programming. Having read different explanations for memory ordering semantics, I would like to clear up what possible reordering may happen. As far as I understood, instructions may be reordered by the compiler (due to optimization when the program is compiled) and CPU (at runtime?).

对于宽松的语义, cpp参考提供了以下示例:

For the relaxed semantics cpp reference provides the following example:

// Thread 1:
r1 = y.load(memory_order_relaxed); // A
x.store(r1, memory_order_relaxed); // B
// Thread 2:
r2 = x.load(memory_order_relaxed); // C 
y.store(42, memory_order_relaxed); // D

据说x和y最初为零时,代码被允许产生r1 == r2 == 42,这是因为尽管A在线程1中先于B排序,而C在线程2中先于D排序,但C之前没有排序. D从y的修改顺序出现在A之前,B从x的修改顺序出现在C之前.怎么会这样这是否意味着C和D已重新排序,所以执行顺序将为DABC?是否可以对A和B重新排序?

It is said that with x and y initially zero the code is allowed to produce r1 == r2 == 42 because, although A is sequenced-before B within thread 1 and C is sequenced before D within thread 2, nothing prevents D from appearing before A in the modification order of y, and B from appearing before C in the modification order of x. How could that happen? Does it imply that C and D get reordered, so the execution order would be DABC? Is it allowed to reorder A and B?

对于获得发布的语义,有以下示例代码:

For the acquire-release semantics there is the following sample code:

std::atomic<std::string*> ptr;
int data;

void producer()
{
    std::string* p  = new std::string("Hello");
    data = 42;
    ptr.store(p, std::memory_order_release);
}

void consumer()
{
    std::string* p2;
    while (!(p2 = ptr.load(std::memory_order_acquire)))
        ;
    assert(*p2 == "Hello"); // never fires
    assert(data == 42); // never fires
}

我想知道我们是否使用宽松的存储顺序而不是获取?我猜想data的值可以在p2 = ptr.load(std::memory_order_relaxed)之前读取,但是p2呢?

I'm wondering what if we used relaxed memory order instead of acquire? I guess, the value of data could be read before p2 = ptr.load(std::memory_order_relaxed), but what about p2?

最后,为什么在这种情况下可以使用宽松的内存顺序?

Finally, why it is fine to use relaxed memory order in this case?

template<typename T>
class stack
{
    std::atomic<node<T>*> head;
 public:
    void push(const T& data)
    {
      node<T>* new_node = new node<T>(data);

      // put the current value of head into new_node->next
      new_node->next = head.load(std::memory_order_relaxed);

      // now make new_node the new head, but if the head
      // is no longer what's stored in new_node->next
      // (some other thread must have inserted a node just now)
      // then put that new head into new_node->next and try again
      while(!head.compare_exchange_weak(new_node->next, new_node,
                                        std::memory_order_release,
                                        std::memory_order_relaxed))
          ; // the body of the loop is empty
    }
};

我的意思是head.load(std::memory_order_relaxed)head.compare_exchange_weak(new_node->next, new_node, std::memory_order_release, std::memory_order_relaxed).

总而言之,我的问题本质上是我什么时候必须关心潜在的重新排序,什么时候不关心?

To summarize all the above, my question is essentially when do I have to care about potential reordering and when I don't?

推荐答案

对于#1,编译器可能会在从x加载之前(没有依赖项)将存储发布到y,即使没有加载,也是如此x的延迟可能会延迟到cpu/内存级别.

For #1, compiler may issue the store to y before the load from x (there are no dependencies), and even if it doesn't, the load from x can be delayed at cpu/memory level.

对于#2,p2将为非零值,但是* p2和数据都不一定具有有意义的值.

For #2, p2 would be nonzero, but neither *p2 nor data would necessarily have a meaningful value.

对于#3,只有一个发布此线程创建的非原子存储的动作,这是一个发行版

For #3 there is only one act of publishing non-atomic stores made by this thread, and it is a release

您应该始终关心重新排序,或者更好的是,不承担任何顺序:C ++和硬件都不从上到下执行代码,它们只尊重依赖关系.

You should always care about reordering, or, better, not assume any order: neither C++ nor hardware executes code top to bottom, they only respect dependencies.

这篇关于无锁编程:重新排序和存储顺序语义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆