我有与以下代码相关的问题
#include <atomic>
#include <thread>
#include <assert.h>
std::atomic<bool> x, y;
std::atomic<int> z;
void write_x_then_y()
{
x.store(true, std::memory_order_relaxed);
y.store(true, std::memory_order_relaxed);
}
void read_y_then_x()
{
while (!y.load(std::memory_order_acquire));
if (x.load(std::memory_order_acquire))
++z;
}
int main()
{
x = false;
y = false;
z = 0;
std::thread a(write_x_then_y);
std::thread b(read_y_then_x);
a.join();
b.join();
assert(z.load() != 0);
}
我能确定assert(z.load()!= 0)总是假的吗? 我认为x.store和y.store没有在数据提供程序线程中重新排序(这是真的吗?)。 出于这个原因,我认为如果加载由x和y存储的值的线程使用memory_order_acquire,则从执行存储操作符的核心的高速缓存中获取x和y的实际值。
答案 0 :(得分:3)
我认为断言可能会失败。 std::memory_order_relaxed
允许编译器对write_x_then_y
内的存储重新排序。 (例如,如果它认为它会因任何原因更快。)因此它可能会在y
之前写x
。整个read_y_then_x
可能发生在这两次写入之间,因此它会y
true
,x
为false
并且不会增加z
}}
答案 1 :(得分:1)
虽然michalsrb已经回答了,但我正在添加我的答案,因为他以“我认为”开始;)。
C ++内存模型允许断言失败。
以下是一些注释:
#include <cassert>
;标准标题不会以.h。 atomic<bool>
和atomic<int>
极有可能(无锁定)POD;通过在全局命名空间中定义它们,它们将使用全零的图像进行初始化;也就是说,即使在到达main之前,它们也将分别具有值false
和0
。因此,在主要开头的三个任务没有效果。另一方面,通常(例如,当原子变量在堆栈上时)显式初始化和赋值之间存在差异:初始初始化是非原子的。赋值变为具有内存顺序seq_cst的存储。因此,更好的风格是(仍然)使用初始化列表(使用= false
等会做同样的事情):
std::atomic<bool> x{false};
std::atomic<bool> y{false};
std::atomic<int> z{0};
std::memory_order_acquire
仅在读取使用store memory_order_release
(包括释放和获取的memory_order_seq_cst
)写入的值时才会导致同步。但是,由于您在不同的线程中没有store
memory_order_release
,因此肯定不会发生任何同步。 main
中的初始化是seq_cst,但这是在创建线程b之前完成的,因此已经存在同步(nl。还 - 同步 - 使用,这非常类似于线程间的Sequenced-Before关系)。因此,使用std::memory_order_relaxed
而不是std::memory_order_acquire
将执行相同的操作并明确使用memory_order_acquire似乎有点奇怪。
那么,因为线程a和b之间没有同步,所以在两个线程看到x和y的变化的顺序之间没有同步,并且线程b在看到x变为真之前可以看到y变为真。
不要试图通过编译器重新排序或硬件管道或任何东西来理解这一点;这是抽象的C ++内存模型“计算机”,它独立于您可能使用的任何实现(编译器)或硬件。事实上,允许进行这种重新排序。考虑到这一点,线程b可以完成并加入,使z仍为其值0。
如果将程序更改为:
,看看会发生什么可能是有益的void write_x_then_y()
{
x.store(true, std::memory_order_relaxed);
y.store(true, std::memory_order_release); // RELEASE HERE
}
void read_y_then_x()
{
while (!y.load(std::memory_order_acquire));
if (x.load(std::memory_order_relaxed))
++z;
}
线程b仍会挂在while
上,直到它为y读取值true
。因此,它读取线程1使用存储memory_order_release
写入的值!请注意,y的加载仍然使用memory_order_acquire
完成。现在进行同步:在我们读取的存储/释放之前写入任何内存位置(也不是y)的所有内容都将在读取后执行读取/获取的线程中可见。换句话说,现在线程a的x.store(true, std::memory_order_relaxed);
在执行x的加载时将在线程b中可见;断言永远不会失败。