我在考虑原子变量是否有可能在获取 - 释放对中加载旧值。 假设我们有原子变量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);
}
如果在线程2加载x(因此存储新值)时函数线程1完成,则线程2是否可以从x加载旧值?换句话说,如果在加载之前完成了对x的实际存储,那么断言可能会触发吗?
据我从互联网上的文章中了解,这是可能的,但我无法理解为什么。内存栅栏由商店生成到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);
}
它会改变什么吗?
感谢。
答案 0 :(得分:5)
您可以将具有释放/获取内存顺序的存储/加载函数视为以下伪代码:
template<class T>
struct weak_atomic
{
void store(T newValue)
{
ReleaseBarrier();
m_value = newValue;
}
T load()
{
T value = m_value;
AcquireBarrier();
return value;
}
volatile T m_value;
}
你说
商店生成的内存栅栏到x 保证清空商店缓冲区
据我所知,释放内存屏障会导致CPU刷新其存储缓冲区,但在将新值应用于x之前将完成。因此,似乎可以通过另一个CPU从x读取旧值。
无论如何,弱原子是非常复杂的区域。在继续进行无锁编程之前,请确保了解内存障碍。
<强> ADDED 强>
看来你仍然对记忆障碍感到困惑。这是他们使用的一个非常常见的例子。
volatile int x;
volatile bool ok;
void thread_1()
{
x = 100;
ok = true;
}
void thread_2()
{
if (ok)
{
assert(x == 100);
}
}
由于无序执行,您可能会得到以下顺序:
thread 1 sets ok to true
thread 2 checks ok is true and reads some garbage from x
thread 1 sets x to 100 but it is too late
另一个可能的顺序:
thread 2 reads some garbage from x
thread 2 checks for ok value
我们可以通过释放解决这个问题并获得内存障碍。
volatile int x;
volatile bool ok;
void thread_1()
{
x = 100;
ReleaseBarrier();
ok = true;
}
void thread_2()
{
if (ok)
{
AcquireBarrier();
assert(x == 100);
}
}
ReleaseBarrier()
保证内存写入不会越过障碍。
这意味着当ok
已包含有效值时,true
仅设置为x
。
AcquireBarrier()
保证内存读取无法跳过屏障。
这意味着仅在检查x
状态后才会读取ok
的值。
这是打算使用发布/获取对的方式。我们可以使用weak_atomic
重写此示例。
volatile int x;
weak_atomic<bool> ok;
void thread_1()
{
x = 100;
ok.store(true);
}
void thread_2()
{
if (ok.load())
{
assert(x == 100);
}
}