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

时间:2016-04-24 15:01:38

标签: c++ c++11 atomic memory-barriers memory-model

我对std::memory_order_acquirestd::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);

根据上述获取和发布的定义,我不明白的是,为什么C ++ 11专门将获取加载相关联,并且< em>发布商店?是的,我已经看过许多示例,这些示例显示了如何使用获取/加载与发布/存储来在线程之间进行同步,但一般来说似乎是获取fences的想法(防止语句后的内存重新排序)和释放围栏(防止语句前的内存重新排序)与加载和存储的想法正交。

所以,例如,为什么编辑器让我说:

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

我意识到我可以使用memory_order_relaxed完成上述操作,然后使用单独的atomic_thread_fence(memory_order_acquire)语句完成上述操作,但同样,为什么我不能直接将商店用于memory_order_acquire?< / p>

可能的用例可能是,如果我想确保某些商店(比如x = 10)在执行某些其他语句之前发生,可能会影响其他线程。

2 个答案:

答案 0 :(得分:13)

假设我写了一些数据,然后我写下了数据现已准备好的指示。至关重要的是,没有其他线程看到数据准备好的指示,看不到数据本身的写入。因此,先前的写入不能超越该写入。

我说我读过一些数据准备好了。在看到数据已经准备就绪的读取之后,我发出的任何读取都是必要的。因此后续的读取不能在读取后面移动。

因此,当您执行同步写入时,通常需要确保之前所做的所有写操作对于看到同步写入的任何人都可见。当你进行同步读取时,通常必须在同步读取之后进行任何读取。

或者,换句话说,获取通常是您可以读取或访问资源的读取,并且后续的读取和写入不得在它之前移动。一个版本通常写着您已完成资源,并且之前的写入不得移动到它之后。

答案 1 :(得分:-2)

std::memory_order_acquire 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

确保所有货物在商店之前没有重新订购。再次,围栏在这里不起作用。

此外,原子操作中的内存顺序可能比内存栅栏便宜,因为它只能确保相对于原子指令的顺序,而不是围栏之前和之后的所有指令。

有关详细信息,另请参阅formal descriptionexplanation