获取-释放对乱序执行
我正在考虑原子变量是否可以加载获取-释放对中的旧值。 假设我们有原子变量 x,并且我们使用释放语义存储该变量,然后使用获取语义加载它,理论上是否可以读取旧值?
std::atomic<int> x = 0;
void thread_1()
{
x.store(1, std::memory_order_release);
}
void thread_2()
{
assert(x.load(std::memory_order_acquire) != 0);
}
如果函数线程 1 在线程 2 加载 x 时完成(因此存储了新值),线程 2 是否可以从 x 加载旧值?换句话说,如果在加载之前完成对 x 的实际存储,是否有可能触发断言?
据我从互联网文章中了解到这是可能的,但我不明白为什么。由store to x生成的内存栅栏保证清空存储缓冲区,而从x加载中获取内存栅栏保证使缓存行无效,因此它必须读取最新值。
添加
这是否意味着获取-释放本身没有任何强制顺序?只有在发布之前完成的所有操作都会在发布之前发生,而在获取之后完成的所有操作都会在发布之后发生,因此获取-释放对强制对其他操作进行排序(为什么?)。我做对了吗? 则断言保证不会再次触发
std::atomic<int> x = 0;
std::atomic<int> y = 0;
void thread_1()
{
y.store(1, std::memory_order_relaxed);
x.store(1, std::memory_order_release);
}
void thread_2()
{
x.load(std::memory_order_acquire);
assert(y.load(std::memory_order_relaxed) != 0);
}
这是否意味着在下面的代码中,如果线程 1 已经完成存储, 。如果我们用 while (x.load() == 0) 替换 x.load ,这将 100% 工作,但我不知道是什么导致它工作。
如果我用下面的代码替换代码会怎么样,
std::atomic<int> x = 0;
void thread_1()
{
x.exchange(1, std::memory_order_acq_rel);
}
void thread_2()
{
assert(x.exchange(0, std::memory_order_acq_rel) != 0);
}
它会改变什么吗?
谢谢。
I'm thinking of whether or not it is possible for atomic variable to load the old value in acquire-release pair.
Let's suppose we have atomic variable x, and we store that variable with release semantics and later load it with acquire semantics is it possible in theory to read the old value?
std::atomic<int> x = 0;
void thread_1()
{
x.store(1, std::memory_order_release);
}
void thread_2()
{
assert(x.load(std::memory_order_acquire) != 0);
}
if function thread 1 is finished when thread 2 loads the x (so the new value is stored) is it possible for thread 2 to load old value from x? In other words if actual store to x is done before the load is it possible for assert to fire?
As far as I understood from articles in internet it is possible, but I cannot understand why. Memory fence generated by store to x guaranties to empty store buffer, while acquire memory fence in load from x is guaranteed to invalidate cache line, so it has to read up-to-date value.
added
Does it mean that acquire-release by itself doesn't have any enforced ordering? It's only anything that was done before release will happen before release and everything that is done after acquire will happen after it, so acquire-release pair enforces ordering on the other operations (why??). Did I get it right? Does it mean that in the code bellow assert is guaranteed to do not fire
std::atomic<int> x = 0;
std::atomic<int> y = 0;
void thread_1()
{
y.store(1, std::memory_order_relaxed);
x.store(1, std::memory_order_release);
}
void thread_2()
{
x.load(std::memory_order_acquire);
assert(y.load(std::memory_order_relaxed) != 0);
}
of course again if the thread 1 was already finished the store. If we replace x.load with while (x.load() == 0) this will 100% work, but I don't know what causes this to work.
And what if I replace the code with code bellow
std::atomic<int> x = 0;
void thread_1()
{
x.exchange(1, std::memory_order_acq_rel);
}
void thread_2()
{
assert(x.exchange(0, std::memory_order_acq_rel) != 0);
}
Does it change anything?
Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您可能会将具有释放/获取内存顺序的存储/加载函数视为以下伪代码:
您说
据我所知,释放内存屏障将导致 CPU 刷新其存储缓冲区,但它将在将新值应用于 x 之前完成。因此,似乎可以通过另一个 CPU 从 x 读取旧值。
无论如何,弱原子是一个非常复杂的领域。在进行无锁编程之前,请确保您了解内存屏障。
添加
看来您仍然对内存障碍感到困惑。这是其用法的一个非常常见的示例。
由于无序执行,您可能会得到以下序列:
另一个可能的序列:
我们可以通过释放和获取内存屏障来修复该问题。
ReleaseBarrier()
保证内存写入不能跳过屏障。这意味着仅当
x
已包含有效值时,ok
才会设置为true
。AcquireBarrier()
保证内存读取不能跳过屏障。这意味着只有在检查
ok
状态后才会读取x
的值。这就是释放/获取对的用途。我们可以用我的
weak_atomic
重写这个示例。You might consider store/load functions with release/acquire memory order as the following pseudo-code:
You said
As I understand, the release memory barrier will cause the CPU to flush its store buffer, but it will be done before applying new value to x. So, it seems possible to read old value from x by another CPU.
Anyway, weak atomics is very complex area. Make sure you understand memory barriers before proceeding with lock-free programming.
ADDED
It seems you are still confused with memory barriers. This is a pretty common example of their usage.
Due to out-of-order execution you may get the following sequence:
Another possible sequence:
We may fix that with release and acquire memory barriers.
ReleaseBarrier()
guarantees that memory writes can't jump over the barrier.It means that
ok
is only set totrue
whenx
already contains valid value.AcquireBarrier()
guarantees that memory reads can't jump over the barrier.It means that the value of
x
is only read after checkingok
state.This is how release/acquire pair is intended to be used. We can rewrite this example with my
weak_atomic
.