2 要操作同一个变量,那么就轮流操作,不会有问题。但是多个线程运行在不同
的核心上,事情就发生很大变化了,比如有两个线程,都需要操作某个变量,比
如同时运行 a=a+1 这个逻辑,期望结果是线程 1 对 a 加了 1,线程 2 要在线程 1
输出结果的基础上继续+1,而由于这两个线程运行在两个独立核心上彼此之间没
有协调,可能导致线程 1 读到的 a 的初始值 0,加 1 之后还没来得及将最新结果
更改到 a 所在的地址之前,线程 2 也读到了 a 的初始值 0,加 1 之后也尝试写入
同样的地址,最后 a 的结果是 1,而不是期望中的 2。解决办法则是对变量 a 加
互斥锁,当某个线程操作 a 之前,先将锁(也是个变量)置为 1,其他线程不断
的扫描锁是不是已经被置为 0,如果是 1 则表示其他人正在操作 a,如果是 0 则
表示其他人已经释放了,那么其将锁改为 1,也就是锁上,自己操作 a,此时读
到的 a 就会是被其他线程更新之后的最新数据了。这个过程叫做 Consistency。
所以,如果多个线程之间完全独立各干各的,没有任何交互,这是最理想的场景,
这就像多台无须联网的独立的计算机各干各的一样,只不过共用了 CPU 和内存。
然而,如果使用了缓存,又使用了共享变量,事情又变得复杂了。线程 1 所
在的核心 1 抢到变量 a 的锁之后会将 a 的内容缓存到核心 1 的缓存中,更新了 a
内容之后,该更新也依然留在缓存中而不是被 flush 到主存。其释放锁之后,线
程 2 抢到 a 的锁并将 a 读入核心 2 的缓存,此时如果不做任何处理,核心 2 从主
存中读到的将是 a 的旧内容,从而计算出错。可以看到,即便是使用了锁来保证
Consistency,也无法避免缓存所带来的一致性问题,后者则被称为 Coherency。
Consistency 由软件来负责,而 Coherency 则要由硬件来负责保证,具体做
法是将每一笔数据更新同步广播出去给所有其他核心/CPU,将它们缓存中的旧内
容作废,收到其他所有核心的回应之后,该更新才被认为成功。所以核心/CPU 之
间需要一个超低时延的网络用于承载这个广播。这个过程对软件完全透明。除了
需要广播作废外,当其他核心需要访问该变量时,拥有该变量最新内容的核心必
须做出应答将该内容推送到发出访问请求的核心。
在很早期的 SMP/UMA 架构下,由于那时的 SMP 总线本身就是一个广播域,任
何核心的访存请求都会被其他所有核心收听到,包括更新了某个地址、读取某个
地址,这样很天然的可以实现 Coherency,比如,当某个核心更新了某个地址之
后,其他核心后台默默的收听(或者说嗅探,Snoop),并在自己缓存中查询自己
评论0