简体   繁体   English

内存栅栏:获取/加载和释放/存储

[英]Memory fences: acquire/load and release/store

My understanding of std::memory_order_acquire and std::memory_order_release is as follows:我对std::memory_order_acquirestd::memory_order_release的理解如下:

Acquire means that no memory accesses which appear after the acquire fence can be reordered to before the fence.获取意味着在获取栅栏之后出现的任何内存访问都不能重新排序到栅栏之前。

Release means that no memory accesses which appear before the release fence can be reordered to after the fence.释放意味着出现释放栅栏之前的内存访问不能重新排序到栅栏之后。

What I don't understand is why with the C++11 atomics library in particular, the acquire fence is associated with load operations, while the release fence is associated with store operations.我不明白的是为什么特别是在 C++11 原子库中,获取栅栏与加载操作相关联,而释放栅栏与存储操作相关联。

To clarify, the C++11 <atomic> library enables you to specify memory fences in two ways: either you can specify a fence as an extra argument to an atomic operation, like:澄清一下,C++11 <atomic>库使您可以通过两种方式指定内存栅栏:您可以将栅栏指定为原子操作的额外参数,例如:

x.load(std::memory_order_acquire);

Or you can use std::memory_order_relaxed and specify the fence separately, like:或者您可以使用std::memory_order_relaxed并单独指定围栏,例如:

x.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);

What I don't understand is, given the above definitions of acquire and release, why does C++11 specifically associate acquire with load , and release with store ?我不明白的是,鉴于上述acquire 和release 的定义,为什么C++11 专门将acquireload关联起来,将releasestore关联起来? Yes, I've seen many of the examples that show how you can use an acquire/load with a release/store to synchronize between threads, but in general it seems that the idea of acquire fences (prevent memory reordering after statement) and release fences (prevent memory reordering before statement) is orthogonal to the idea of loads and stores.是的,我已经看过许多示例,它们展示了如何使用带有释放/存储的获取/加载在线程之间进行同步,但总的来说,获取栅栏(防止语句后重新排序)和释放的想法似乎是栅栏(在语句之前防止内存重新排序)与加载和存储的想法是正交的。

So, why, for example, won't the compiler let me say:那么,例如,为什么编译器不让我说:

x.store(10, std::memory_order_acquire);

I realize I can accomplish the above by using memory_order_relaxed , and then a separate atomic_thread_fence(memory_order_acquire) statement, but again, why can't I use store directly with memory_order_acquire ?我意识到我可以通过使用memory_order_relaxed来完成上述memory_order_relaxed ,然后使用单独的atomic_thread_fence(memory_order_acquire)语句,但同样,为什么我不能直接将 store 与memory_order_acquire一起memory_order_acquire

A possible use case for this might be if I want to ensure that some store, say x = 10 , happens before some other statement executes that might affect other threads.一个可能的用例可能是,如果我想确保某些存储(例如x = 10 )发生其他可能影响其他线程的其他语句执行之前

Say I write some data, and then I write an indication that the data is now ready. 假设我写了一些数据,然后我写了一个数据现已准备好的指示。 It's imperative that no other thread who sees the indication that the data is ready not see the write of the data itself. 至关重要的是,没有其他线程看到数据准备好的指示,看不到数据本身的写入。 So prior writes cannot move past that write. 因此,先前的写入不能超过该写入。

Say I read that some data is ready. 假设我读到一些数据准备好了。 It's imperative that any reads I issue after seeing that take place after the read that saw that the data was ready. 在看到数据已经准备就绪的读取之后,我发出的任何读取都是必要的。 So subsequent reads cannot move behind that read. 因此后续的读取不能移动到读取之后。

So when you do a synchronized write, you typically need to make sure that all writes you did before that are visible to anyone who sees the synchronized write. 因此,当您执行同步写入时,通常需要确保在查看同步写入的任何人都可以看到之前执行的所有写入。 And when you do a synchronized read, it's typically imperative that any reads you do after that take place after the synchronized read. 当您进行同步读取时,通常必须在同步读取之后执行任何读取操作。

Or, to put it another way, an acquire is typically reading that you can take or access the resource, and subsequent reads and writes must not be moved before it. 或者,换句话说,获取通常是您可以读取或访问资源的读取,并且后续的读取和写入不得在它之前移动。 A release is typically writing that you are done with the resource, and preceding writes must not be moved to after it. 一个版本通常写着您已完成资源,并且之前的写入不得移动到它之后。

我认为Jeff Preshing 的这篇文章可以回答您的问题。

std::memory_order_acquire fence only ensures all load operation after the fence is not reordered before any load operation before the fence, thus memory_order_acquire cannot ensure the store is visible for other threads when after loads are executed. std::memory_order_acquire fence仅确保在fence之前的任何加载操作之前没有重新排序fence之后的所有加载操作,因此memory_order_acquire 无法确保在执行加载之后其他线程的存储是可见的。 This is why memory_order_acquire is not supported for store operation, you may need memory_order_seq_cst to achieve the acquire of store. 这就是存储操作不支持memory_order_acquire原因,您可能需要memory_order_seq_cst来实现存储的获取。

As an alternative, you may say 作为替代方案,您可以说

x.store(10, std::memory_order_releaxed);
x.load(std::memory_order_acquire);  // this introduce a data dependency

to ensure all loads not reordered before the store. 确保所有货物在商店之前没有重新订购。 Again, the fence not work here. 再次,围栏在这里不起作用。

Besides, memory order in atomic operation could be cheaper than a memory fence, because it only ensures the order relative to the atomic instruction, not all instruction before and after the fence. 此外,原子操作中的内存顺序可能比内存栅栏便宜,因为它只能确保相对于原子指令的顺序,而不是围栏之前和之后的所有指令。

See also formal description and explanation for detail. 又见形式化描述解释的细节。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM