我从以下网站获取了关于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可以说“好吧我看到0即使线程a已经完成所以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';
}
答案 0 :(得分:3)
因为读 - 修改 - 写操作有特殊保证。
根据标准[atomics.order] paragraph 11:
原子读 - 修改 - 写操作应始终读取与读 - 修改 - 写操作相关的写操作之前写入的最后一个值(按修改顺序)。
答案 1 :(得分:1)
因此,通过在评论中看到不同的东西,你的意思是线程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
当我们说&#34;看&#34;我们的意思是线程可以执行负载。在一个线程中不能同时执行两个加载。那么线程C怎么可能看到x == 1 然后看y == 0而线程D看x == 0 然后看y == 1?当内存处于状态S1时,线程C执行两个加载,而线程D在状态S0处看到x
,然后在状态S2看到y
。
在您的示例代码中,发生的是Thread C加载x然后加载y,并且Thread D重复加载y,直到它为true然后加载x。因此,在y == 1之后,在整个订单中保证x==1
。
正如Minee在其评论中所说,如果代替顺序一致性内存顺序使用获取/释放内存顺序,则无法预期:获取/释放语义并不意味着任何总排序,而且没有发生之前商店与x
和商店之间的关系y
。所以断言z.load()!=0
可以解雇。
答案 2 :(得分:0)
我的问题是线程c和线程d怎么可能看到 不同的东西?
理论上是允许的,实际上,如果您有多个原子变量并且某些操作没有memory_order_seq_cst
顺序,则有可能发生。
因此在您的代码中不可能在所有操作上都使用memory_order_seq_cst
(仅在某些操作上使用它是危险的,因为它可能导致细微的错误)。
例如,线程b可能会说“好吧,即使线程a是 已经完成,因此z再次变为0 + 1”。
否。
但是无论如何,单个原子变量上允许的操作与内存排序无关,这会影响其余内存的可见性,并且不会影响正在操作的对象< / em>。
如果您只有一个原子变量而没有其他共享状态,则可见性是无关紧要的,因为没有任何要显示的内容。
[关于标准说明的注意事项:
该标准意味着至少在理论上,断言并非在所有情况下都适用于宽松的操作。但是标准在线程上是疯狂的:除非我的断言正确,否则它定义不正确。
无论如何,该标准指出,在实践中,实现应避免在我的断言为假的情况下允许执行。而且在实践中它们不会在任何地方发生。]