这是一个后续问题this one https://stackoverflow.com/questions/59626494/understanding-memory-order-acquire-and-memory-order-release-in-c11/59629166#59629166.
我想准确地弄清楚指令顺序的含义,以及它是如何受到std::memory_order_acquire
, std::memory_order_release
etc...
在我链接的问题中,已经提供了一些细节,但我觉得提供的答案实际上并不是关于顺序(这更多是我想要的),而是有点激励为什么这是必要的等。
我将引用相同的示例作为参考
#include <thread>
#include <atomic>
#include <cassert>
#include <string>
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
}
int main()
{
std::thread t1(producer);
std::thread t2(consumer);
t1.join(); t2.join();
}
简而言之,我想弄清楚两行的指令顺序到底发生了什么
ptr.store(p, std::memory_order_release);
and
while (!(p2 = ptr.load(std::memory_order_acquire)))
根据文档重点关注第一个
...当前线程中的读取或写入操作在此存储之后不能重新排序...
我看了一些演讲来理解这个排序问题,我明白为什么它现在很重要。我还不太清楚编译器如何翻译顺序规范,我认为文档给出的示例也不是特别有用,因为在运行的线程中进行存储操作之后producer
没有其他指令,因此无论如何都不会重新排序。然而,我也可能误解了,它们是否可能意味着等效的组装
std::string* p = new std::string("Hello");
data = 42;
ptr.store(p, std::memory_order_release);
翻译的前两行将永远不会在原子存储之后移动?
同样,在运行生产者的线程中,是否有可能在原子加载之前没有任何断言(或等效程序集)被移动?假设我在存储之后有第三条指令,那些已经在原子加载之后的指令会发生什么?
我还尝试编译此类代码以保存中间汇编代码-S
旗帜,但它很大,我无法真正弄清楚。
再次澄清,这个问题是关于如何排序的,而不是关于为什么这些机制有用或必要的。