在JCIP第15.2节中B. Goetz提到在现代处理器中有一组指令,如比较和交换(CAS),它允许我们执行非阻塞更新操作。
特别是他说
CAS有三个操作数 - 一个操作的存储位置V,即预期的操作数 旧值A和新值B.CAS原子地将V更新为新值B, 但仅当V中的值与预期的旧值A匹配时才<。
现在考虑一个多处理器系统。如果两个不同的处理器尝试在同一个内存位置上完全相同的时间进行比较和交换,会发生什么?
处理器彼此不了解 。这种情况如何管理?
答案 0 :(得分:0)
处理器彼此不了解。
我很确定他们知道其他处理器,看看MSI协议......
https://en.wikipedia.org/wiki/MSI_protocol
有更多现代版本的MSI即MESI等专门解决这个问题。想象一下,CPU可以在没有机制的情况下改变数据的世界,以确保数据在CPU之间保持一致。
计算机科学中只有两件事:缓存失效,命名事物和一个错误。
答案 1 :(得分:0)
处理器彼此不了解。
处理器不知道它们只是芯片上的硅片。
如果两个不同的处理器尝试在同一个内存位置上完全相同的时间进行比较和交换,会发生什么?
如果CPU的缓存具有对缓存行的独占访问权限,则可以直接执行更新。如果它没有独占访问权限。 L2缓存一致性总线可以从另一个CPU的缓存或L3缓存中获取,然后它可以执行操作,也可以不执行操作。 CAS的不同之处在于它不会长时间阻塞。它可以返回false
表示它无法获取缓存行,但是请调用它,处理器应该在将来的某个时刻抓住它。
这种情况如何管理?
简而言之,L2缓存一致性总线传输此信息并确保线程安全操作正常工作。
答案 2 :(得分:0)
每个进程都有自己的内存,对于内部通信,进程确实使用共享内存。在共享内存的情况下,我们需要在该共享内存中同步读/写。按照引用:
CAS有三个操作数 - 一个操作的内存位置V,即 预期旧值A和新值B. CAS原子地将V更新为 新值B ,但仅当V中的值与预期的旧值匹配时 价值A。
因为这是原子的,不仅是原子到单个处理器而且是多个。这里的原子性是在公共汽车上实现的。 CAS
锁定总线以确保操作的原子性。
要深入了解所有这些如何工作以及使用哪些算法来确保原子性,请参阅8.1.3 Multiprocessor Synchronization
答案 3 :(得分:0)
CAS操作最终依赖于特定的硬件指令,因此实际的实现是特定于硬件的。如果您想了解详细信息,则需要检查您正在运行的特定体系结构。
在最流行的x86架构中,您可以将CAS操作视为使用锁定,但在硬件中更精细的层次。
E.g。实现CAS操作的一种方法是LOCK CMPXCHG
。 LOCK
是一个指令前缀,用于确保在伴随指令的持续时间内对相应的存储单元进行独占访问。在这种情况下,伴随的指令是CMPXCHG
,它可以将给定存储器位置的值作为单个指令进行字面比较和交换。
当您从几个不同的线程执行myAtomicInt.compareAndSet(0, 1)
并导致来自几个不同处理器的LOCK CMPXCHG
时,一个幸运的处理器将首先获得排他性并成功执行其操作。对此内存位置的锁定访问是可线性化的,因此稍后将获得排他性的剩余处理器将使其CAS失败(除非交错更改引入ABA problem)。
仍然可以省略内存总线锁定(请参阅英特尔®64和IA-32架构中的8.1.4 Effects of a LOCK Operation on Internal Processor Caches 软件开发人员手册
执行CAS操作的另一种硬件方法是LL / SC(加载链接/存储条件),它使用稍微不同的机制,具有不同的特性。
粗略地说,它将CAS分为两部分 - 初始加载和条件存储,如果初始加载和存储之间的相关内存发生了变化,它会丢弃存储。这可以让您避免ABA问题,但它具有不同的可伸缩性特征。
Here's使用LDREX
和STREX
指令在ARM中使用LL / SC的示例。
某些体系结构(如SPARC)没有专用的CAS指令。在他们身上,CAS需要作为指令和最终记忆障碍的组合来实现。