简体   繁体   English

C++ memory_order_acquire/release问题

[英]C++ memory_order_acquire/release questions

I recently learn about c++ six memory orders, I felt very confusing about memory_order_acquire and memory_order_release , here is an example from cpp:最近了解了 c++ 六个 memory 订单,对memory_order_acquirememory_order_release感到很困惑,下面是来自 cpp 的示例:

#include <thread>
#include <atomic>
#include <cassert>
 
std::atomic<bool> x = {false};
std::atomic<bool> y = {false};
std::atomic<int> z = {0};
 
void write_x() { x.store(true, std::memory_order_seq_cst); }
void write_y() { y.store(true, std::memory_order_seq_cst); }
 
void read_x_then_y() {
     while (!x.load(std::memory_order_seq_cst));

     if (y.load(std::memory_order_seq_cst)) 
         ++z;
}
 
void read_y_then_x() {
     while (!y.load(std::memory_order_seq_cst));

     if (x.load(std::memory_order_seq_cst))
        ++z;
}
 
int main() {
    std::thread a(write_x);
    std::thread b(write_y);
    std::thread c(read_x_then_y);
    std::thread d(read_y_then_x);

    a.join(); b.join(); c.join(); d.join();

    assert(z.load() != 0);  // will never happen
}

In the cpp reference page, it says:在 cpp 参考页面中,它说:

This example demonstrates a situation where sequential ordering is necessary.此示例演示了需要顺序排序的情况。

Any other ordering may trigger the assert because it would be possible for the threads c and d to observe changes to the atomics x and y in opposite order.任何其他排序都可能触发断言,因为线程 c 和 d 可能以相反的顺序观察原子 x 和 y 的变化。

So my question is why memory_order_acquire and memory_order_release can not be used here?所以我的问题是为什么不能在这里使用memory_order_acquirememory_order_release And what semantics does memory_order_acquire and memory_order_release provide? memory_order_acquire 和 memory_order_release 提供什么语义?

some references: https://en.cppreference.com/w/cpp/atomic/memory_order https://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync一些参考资料: https://en.cppreference.com/w/cpp/atomic/memory_order https://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync

Sequential consistency provides a single total order of all sequentially consistent operations.顺序一致性提供了所有顺序一致操作的单一总顺序 So if you have a sequentially consistent store in thread A, and a sequentially consistent load in thread B, and the store is ordered before the load (in said single total order), then B observes the value stored by A. So basically sequential consistency guarantees that the store is "immediately visible" to other threads.因此,如果您在线程 A 中有一个顺序一致的存储,并且在线程 B 中有一个顺序一致的加载,并且存储在加载之前排序(按所述单个总顺序),那么 B 会观察 A 存储的值。所以基本上是顺序一致性保证存储对其他线程“立即可见”。 A release store does not provide this guarantee.发行商店提供此保证。

As Peter Cordes pointed out correctly, the term "immediately visible" is rather imprecise.正如 Peter Cordes 正确指出的那样,“立即可见”一词相当不精确。 The "visibility" stems from the fact that all seq-cst operations are totally ordered, and all threads observe that order. “可见性”源于这样一个事实,即所有 seq-cst 操作都是完全有序的,并且所有线程都遵守该顺序。 Since the store and the load are totally ordered, the value of a store becomes visible before a subsequent load (in the single total order) is executed.由于存储和加载是完全排序的,因此在执行后续加载(以单个总顺序)之前,存储的值变得可见。

There exists no such total order between acquire/release operations in different threads, so there is not visibility guarantee.不同线程中的获取/释放操作之间不存在这样的总顺序,因此不存在可见性保证。 The operations are only ordered once an acquire-operations observes the value from a release-operation, but there is no guarantee when the value of the release-operation becomes visible to the thread performing the acquire-operation.只有在获取操作观察到释放操作的值时,操作才会被排序,但不能保证释放操作的值何时对执行获取操作的线程可见。

Let's consider what would happen if we were to use acquire/release in this example:让我们考虑一下如果我们在这个例子中使用获取/释放会发生什么:

void write_x() { x.store(true, std::memory_order_release); }
void write_y() { y.store(true, std::memory_order_release); }
 
void read_x_then_y() {
     while (!x.load(std::memory_order_acquire));

     if (y.load(std::memory_order_acquire)) 
         ++z;
}
 
void read_y_then_x() {
     while (!y.load(std::memory_order_acquire));

     if (x.load(std::memory_order_acquire))
        ++z;
}
 
int main() {
    std::thread a(write_x);
    std::thread b(write_y);
    std::thread c(read_x_then_y);
    std::thread d(read_y_then_x);

    a.join(); b.join(); c.join(); d.join();

    assert(z.load() != 0);  // can actually happen!!
}

Since we have no guarantee about visibility, it could happen that thread c observes x == true and y == false , while at the same time thread d could observe y == true and x == false .由于我们无法保证可见性,因此线程c可能会观察到x == truey == false ,而同时线程d可能会观察到y == truex == false So neither thread would increment z and the assertion would fire.所以两个线程都不会增加z并且断言会触发。

For more details about the C++ memory model I can recommend this paper which I have co-authored: Memory Models for C/C++ Programmers For more details about the C++ memory model I can recommend this paper which I have co-authored: Memory Models for C/C++ Programmers

You can use aquire/release when passing information from one thread to another - this is the most common situation.在将信息从一个线程传递到另一个线程时,您可以使用 aquire/release - 这是最常见的情况。 No need for sequential requirements on this one.不需要对此有顺序要求。

In this example there are a bunch of threads.在这个例子中,有一堆线程。 Two threads make write operation while third roughly tests whether x was ready before y and fourth tests whether y was ready before x .两个线程进行写操作,而第三个线程粗略地测试x是否在y之前准备好,第四个线程测试y是否在x之前准备好。 Theoretically one thread may observe that x was modified before y while another sees that y was modified before x .从理论上讲,一个线程可能会观察到xy之前被修改,而另一个线程会看到yx之前被修改。 Not entirely sure how likely it is.不完全确定它的可能性有多大。 This is an uncommon usecase.这是一个不常见的用例。

Edit: you can visualize the example: assume that each threads is run on a different PC and they communicate via a network.编辑:您可以可视化该示例:假设每个线程都在不同的 PC 上运行,并且它们通过网络进行通信。 Each pair of PCs has a different ping to each other.每对 PC 之间都有不同的 ping。 Here it is easy to make an example where it is unclear which event occurred first x or y as each PC will see the events occur in different order.这里很容易举一个例子,其中不清楚哪个事件首先发生在xy上,因为每台 PC 会看到事件以不同的顺序发生。

I am not sure on sure on which architectures this effect may occur but there are complex ones where two different processors are conjoined.我不确定这种效果可能会发生在哪些架构上,但有一些复杂的架构,其中两个不同的处理器结合在一起。 Surely communication between the processors is slower than between cores of each processor.当然,处理器之间的通信比每个处理器的内核之间的通信要慢。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 C++ 标准如何使用 memory_order_acquire 和 memory_order_release 防止自旋锁互斥锁中的死锁? - How C++ Standard prevents deadlock in spinlock mutex with memory_order_acquire and memory_order_release? 为什么 c++ singleton 需要 memory_order_acquire - why c++ singleton need memory_order_acquire 与 memory_order_acquire 和 memory_order_release 的原子交换 - atomic exchange with memory_order_acquire and memory_order_release 理解 C++11 中的 `memory_order_acquire` 和 `memory_order_release` - Understanding `memory_order_acquire` and `memory_order_release` in C++11 C++11 memory_order_acquire 和 memory_order_release 语义? - C++11 memory_order_acquire and memory_order_release semantics? 可以分别使用std :: memory_order_acquire和memory_order_release吗? - Can std::memory_order_acquire and memory_order_release be used separately? 什么时候可以从 compare_exchange 中安全删除 memory_order_acquire 或 memory_order_release? - When can memory_order_acquire or memory_order_release be safely removed from compare_exchange? memory_order_consume 和 memory_order_acquire 的区别 - Difference between memory_order_consume and memory_order_acquire CPU如何提供memory_order_acquire保证的内容? - How does CPU provides what memory_order_acquire guarantees? memory_order_acquire真的足以锁定自旋锁吗? - Is memory_order_acquire really sufficient for locking a spinlock?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM