无特殊原子指令时,多处理器如何实现内存一致共享?
首先明确:没有原子比较交换(CAS)这类复合原子指令,确实没法高效实现传统的互斥锁(mutex),但依然存在一些方法来保证多核间的内存一致性和基本同步,只是这些方案要么性能拉胯,要么有明显的局限性。
总线锁定
早期多核系统的一种原始方案:处理器在执行内存操作前,先锁定整个前端总线,让其他所有处理器的内存请求都处于等待状态。这样一来,当前处理器的写操作(哪怕是普通写)对其他核来说就是原子性的,能保证内存一致。但问题是总线锁定会阻塞整个系统的内存访问,性能损耗极大,现在已经被淘汰,只适合极端简单的低并发场景。软件同步算法(Dekker/Peterson算法)
这是完全基于普通内存操作的同步方案,不需要任何原子复合指令。以Peterson算法为例,针对两个线程的互斥场景:- 定义两个共享flag变量(标记线程是否想进入临界区)和一个turn变量(标记当前轮到哪个线程进入)。
- 线程A先将自己的flag设为true,把turn设为线程B的ID,然后循环等待:要么线程B的flag为false,要么turn值是自己的ID。
- 进入临界区完成操作后,再将自己的flag设为false。
这类算法依赖内存模型的可见性(可能需要配合内存屏障指令来确保变量更新被其他核看到),但只能支持固定数量的线程(比如Peterson算法原生只支持2线程),扩展性极差,高并发下效率很低。
内存屏障配合简单原子写
如果处理器支持内存屏障指令(如mfence、sfence),可以用普通原子写+内存屏障来保证内存可见性和顺序性。比如:- 写线程更新共享变量后,执行写屏障,确保所有写操作都刷入主存,不会被处理器重排序。
- 读线程读取共享变量前,执行读屏障,确保读到的是主存中的最新值,不是本地缓存的旧数据。
但这种方式只能解决内存一致性问题,没法实现严格的互斥——多个线程同时写同一个变量时,依然会出现覆盖。它只能用于一些不需要互斥的同步场景,比如简单的状态通知。
依赖缓存一致性协议的软同步
现代多核系统基本都有MESI这类缓存一致性协议,缓存行的状态转换(比如从共享态转为独占态)本身是原子的。可以利用这一点:线程先尝试将共享变量所在的缓存行拉到自己的独占状态,再执行写操作。这时候其他核的缓存行会被标记为失效,能保证内存一致。但这种方式本质上还是依赖硬件的底层原子操作,而且软件层面没法直接控制缓存行状态,只能通过内存操作间接触发,同样没法高效实现复杂的同步逻辑。
总结来说:没有CAS这类原子复合指令,确实能实现多核内存一致和基础同步,但这些方案要么性能损耗大,要么扩展性差,要么只能处理有限场景。这也是为什么现代CPU都标配原子复合指令的原因——它们是高效实现互斥锁、高并发同步的基础。
内容的提问来源于stack exchange,提问作者FourierFlux

