[英]Memory fences: acquire/load and release/store
我对std::memory_order_acquire
和std::memory_order_release
的理解如下:
获取意味着在获取栅栏之后出现的任何内存访问都不能重新排序到栅栏之前。
释放意味着出现在释放栅栏之前的内存访问不能重新排序到栅栏之后。
我不明白的是为什么特别是在 C++11 原子库中,获取栅栏与加载操作相关联,而释放栅栏与存储操作相关联。
澄清一下,C++11 <atomic>
库使您可以通过两种方式指定内存栅栏:您可以将栅栏指定为原子操作的额外参数,例如:
x.load(std::memory_order_acquire);
或者您可以使用std::memory_order_relaxed
并单独指定围栏,例如:
x.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
我不明白的是,鉴于上述acquire 和release 的定义,为什么C++11 专门将acquire与load关联起来,将release与store关联起来? 是的,我已经看过许多示例,它们展示了如何使用带有释放/存储的获取/加载在线程之间进行同步,但总的来说,获取栅栏(防止语句后重新排序)和释放的想法似乎是栅栏(在语句之前防止内存重新排序)与加载和存储的想法是正交的。
那么,例如,为什么编译器不让我说:
x.store(10, std::memory_order_acquire);
我意识到我可以通过使用memory_order_relaxed
来完成上述memory_order_relaxed
,然后使用单独的atomic_thread_fence(memory_order_acquire)
语句,但同样,为什么我不能直接将 store 与memory_order_acquire
一起memory_order_acquire
?
一个可能的用例可能是,如果我想确保某些存储(例如x = 10
)发生在其他可能影响其他线程的其他语句执行之前。
假设我写了一些数据,然后我写了一个数据现已准备好的指示。 至关重要的是,没有其他线程看到数据准备好的指示,看不到数据本身的写入。 因此,先前的写入不能超过该写入。
假设我读到一些数据准备好了。 在看到数据已经准备就绪的读取之后,我发出的任何读取都是必要的。 因此后续的读取不能移动到读取之后。
因此,当您执行同步写入时,通常需要确保在查看同步写入的任何人都可以看到之前执行的所有写入。 当您进行同步读取时,通常必须在同步读取之后执行任何读取操作。
或者,换句话说,获取通常是您可以读取或访问资源的读取,并且后续的读取和写入不得在它之前移动。 一个版本通常写着您已完成资源,并且之前的写入不得移动到它之后。
我认为Jeff Preshing 的这篇文章可以回答您的问题。
std::memory_order_acquire
fence仅确保在fence之前的任何加载操作之前没有重新排序fence之后的所有加载操作,因此memory_order_acquire
无法确保在执行加载之后其他线程的存储是可见的。 这就是存储操作不支持memory_order_acquire
原因,您可能需要memory_order_seq_cst
来实现存储的获取。
作为替代方案,您可以说
x.store(10, std::memory_order_releaxed);
x.load(std::memory_order_acquire); // this introduce a data dependency
确保所有货物在商店之前没有重新订购。 再次,围栏在这里不起作用。
此外,原子操作中的内存顺序可能比内存栅栏便宜,因为它只能确保相对于原子指令的顺序,而不是围栏之前和之后的所有指令。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.