我已经听过并阅读了有关的几篇文章,演讲和stackoverflow问题std::atomic
,并且我想确保自己已经很好地理解了。由于MESI(或派生的)高速缓存一致性协议,存储缓冲区,使队列无效等可能存在延迟,因此我仍然对高速缓存行的可见性感到困惑。
我读到x86具有更强的内存模型,并且如果缓存失效被延迟,x86可以还原启动的操作。但是我现在只对我作为独立于平台的C ++程序员应该承担的兴趣感兴趣。
[T1:线程1 T2:线程2 V1:共享原子变量]
我了解std :: atomic可以保证,
(1)在变量上不发生数据争用(由于对缓存行的独占访问)。
(2)取决于我们使用哪种memory_order,它(在有障碍的情况下)保证发生顺序一致性(在障碍之前,之后或之后)。
(3)在T1上执行原子write(V1)之后,T2上的原子RMW(V1)将是连贯的(其缓存行将已用T1上的写入值进行更新)。
但是正如缓存一致性入门所述,
所有这些事情的含义是,默认情况下,加载可以获取过时的数据(如果相应的失效请求位于失效队列中)
那么,以下正确吗?
(4)std::atomic
不保证T2在T1上执行原子write(V)之后不会读取原子read(V)上的“陈旧”值。
问题(4)是否正确:如果无论延迟如何,在T1上进行原子写入都会使高速缓存行无效,那么当原子RMW操作而不是在原子读取上进行操作时,T2为什么要等待无效生效?
问题(4)是否错误:线程何时可以在执行过程中读取“过时”值并且“可见”?
非常感谢您的回答
更新1
所以看来我在(3)上错了。想象以下交织,初始V1 = 0:
T1: W(1)
T2: R(0) M(++) W(1)
即使在这种情况下,保证T2的RMW完全在W(1)之后发生,它仍然可以读取“过时的”值(我错了)。据此,atomic不能保证完全的缓存一致性,而只能保证顺序一致性。
更新2
(5)现在想象这个例子(x = y = 0并且是原子的):
T1: x = 1;
T2: y = 1;
T3: if (x==1 && y==0) print("msg");
根据我们所说的,看到屏幕上显示的“ msg”不会为我们提供T1之后执行T2之外的信息。因此,以下任一处决都可能发生:
- T1 <T3 <T2
- T1 <T2 <T3(其中T3看到x = 1但还没有y = 1)
那正确吗?
(6)如果线程始终可以读取“过时”的值,那么如果采用典型的“发布”方案,但不是发出某些数据准备就绪的信号,而是执行相反的操作(删除数据)会发生什么?
T1: delete gameObjectPtr; is_enabled.store(false, std::memory_order_release);
T2: while (is_enabled.load(std::memory_order_acquire)) gameObjectPtr->doSomething();
直到看到is_enabled为false为止,T2仍将使用已删除的ptr。
(7)另外,线程可能会读取“过时”的值,这意味着不能仅使用一个无锁原子权限来实现互斥锁吗?这将需要线程之间的同步机制。是否需要可锁定的原子?