我从以下示例中获取了有关std::memory_order_seq_cst的示例:
http://en.cppreference.com/w/cpp/atomic/memory_order
#include <thread>
#include <atomic>
#include <cassert>
std::atomic<bool> x = {false};
std::atomic<bool> y = {false};
std::atomic<int> z = {0};
void write_x()
{
x.store(true, std::memory_order_seq_cst);
}
void write_y()
{
y.store(true, std::memory_order_seq_cst);
}
void read_x_then_y()
{
while (!x.load(std::memory_order_seq_cst))
;
if (y.load(std::memory_order_seq_cst)) {
++z;
}
}
void read_y_then_x()
{
while (!y.load(std::memory_order_seq_cst))
;
if (x.load(std::memory_order_seq_cst)) {
++z;
}
}
int main()
{
std::thread a(write_x);
std::thread b(write_y);
std::thread c(read_x_then_y);
std::thread d(read_y_then_x);
a.join(); b.join(); c.join(); d.join();
assert(z.load() != 0); // will never happen
}
Acquire/Release versus Sequentially Consistent memory order问题中也提到了此示例。
我的问题是线程c和线程d怎么可能看到不同的东西?如果可能,为什么下面的这个简单示例始终得出z = 3?例如,线程b可能会说:“即使线程a已经完成,我仍然看到0,所以z再次变为0 + 1”。
#include <atomic>
#include <iostream>
std::atomic<int> z = {0};
void increment()
{
z.fetch_add(1, std::memory_order_relaxed);
}
int main()
{
std::thread a(increment);
std::thread b(increment);
std::thread c(increment);
a.join(); b.join(); c.join();
std::cout << z.load() << '\n';
}
最佳答案
因此,通过在注释中看到不同的内容,您的意思是线程C看到x == 1,y == 0,线程D看到x == 0和y == 1。具有顺序一致性是否可能?
让我们假设这个总顺序(修改是这些符号存储状态之间的转换):
{x==0,y==0} : S0
{x==1,y==0} : S1
{x==1,y==1} : S2
当我们说“see”时,是指线程潜在地执行负载。一个线程不能同时执行两个加载。那么,线程C看到x == 1 然后看到y == 0以及线程D看到x == 0 然后看到y == 1的可能性如何?线程C在内存处于状态S1时执行两次加载,线程D在状态S0处看到
x
,然后在状态S2处看到y
。在您的示例代码中,发生的情况是线程C加载x然后加载y,线程D重复加载y,直到它为true然后加载x。因此,在y == 1之后,可以保证
x==1
处于此总顺序。正如Minee在其评论中所说,如果使用顺序一致性存储顺序来代替获取/释放存储顺序,则不会有什么期望:获取/释放语义并不意味着任何总排序,而且在存储与存储之间的关系之前不会发生任何事情。
x
并将其存储到y
。因此断言z.load()!=0
可能会触发。