我正在阅读“C++ concurrency in action”,我正在阅读 ACQUIRE-RELEASE ORDERING 部分中的代码,示例是
#include <atomic>
#include <thread>
#include <assert.h>
std::atomic<bool> x,y;
std::atomic<int> z;
void write_x()
{
x.store(true,std::memory_order_release);
}
void write_y()
{
y.store(true,std::memory_order_release);
}
void read_x_then_y()
{
while(!x.load(std::memory_order_acquire));
if(y.load(std::memory_order_acquire)) # 1
++z;
}
void read_y_then_x()
{
while(!y.load(std::memory_order_acquire));
if(x.load(std::memory_order_acquire)) # 2
++z;
}
int main()
{
x=false;
y=false;
z=0;
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);
}
那么,作者的分析是
在这种情况下,断言“assert(z.load()!=0);”可以触发(就像在宽松排序的情况下一样),因为 x 的负载(在备注 2 中)和 x 的负载都是可能的y(在注释 1 中)读为 false。 x 和 y 是由不同的线程编写的,因此在每种情况下从释放到获取的顺序对其他线程中的操作没有影响。图 5.6 显示了代码中发生之前的关系,以及可能的结果,其中两个阅读线程各自有不同的视图 世界。这是可能的,因为没有“happens-before”关系来强制排序,如前所述,图 5.6 是 ,
我无法理解图5.6从左到右的第二个子集中的y.load为什么会发生在y.store之前,根据书,具有此内存顺序的释放与获取同步,因此y.store 应该发生在 y.load 之前,并且获取释放排序是宽松排序的一个进步;仍然没有总的操作顺序,那么“仍然没有总的操作顺序”属性是否调用 y.load 可以发生在 y.store 之前?
acquire
不与任何 release
“同步”。
与
同步如果线程 A 中的原子存储是释放操作,则线程 B 中对同一变量的原子加载是获取操作, 并且线程 B 中的加载读取线程 A 中的存储写入的值,然后存储线程 A 中与线程 B 中的负载同步。
强调我的。
只有线程2读取了线程1写入的值,线程2才会与线程1同步,但仍然与线程3或线程4无关,与它们不同步。
想象一台有 2 个核心和 4 个超线程的机器。
核心 1 具有前两个线程,它存储
x=true
并读取 x=true
,但它可以读取 y=true
或 y=false
,因为它与线程 4 不同步。
同时在核心 2 上,其他两个线程正在运行,也写入
y = true
并读取 y = true
,但它可以读取 x = true
或 x = false
,因为它再次没有与线程 4 同步,也许是核心 1 中的存储尚未到达主内存,或者仍在存储缓冲区中,或者缓存仍在刷新中,等等...
在这种情况下,内存模型不能保证两个内核将同步,并且可能会发生
z = 0
。