从ObjectPool到CAS指令( 三 )

lock前缀的指令在执行期间会锁住总线,使得其它处理器暂时无法通过总线访问内存,很显然,这个开销很大 。

  • 现在 - P6以后时代(锁缓存),在新的处理器中,Intel使用缓存锁定来保证指令执行的原子性,缓存锁定将大大降低lock前缀指令的执行开销 。
  • 现在这里的锁缓存(Cache Locking)就是用了Ringbus + MESI协议 。
    MESI协议是 Cacheline 四种状态的首字母的缩写,分别是修改(Modified)态、独占(Exclusive)态、共享(Shared)态和失效(Invalid)态 。Cache 中缓存的每个 Cache Line 都必须是这四种状态中的一种 。
    修改态(Modified),如果该 Cache Line 在多个 Cache 中都有备份,那么只有一个备份能处于这种状态,并且“dirty”标志位被置上 。拥有修改态 Cache Line 的 Cache 需要在某个合适的时候把该 Cache Line 写回到内存中 。但是在写回之前,任何处理器对该 Cache Line在内存中相对应的内存块都不能进行读操作 。Cache Line 被写回到内存中之后,其状态就由修改态变为共享态 。
    独占态(Exclusive),和修改状态一样,如果该 Cache Line 在多个 Cache 中都有备份,那么只有一个备份能处于这种状态,但是“dirty”标志位没有置上,因为它是和主内存内容保持一致的一份拷贝 。如果产生一个读请求,它就可以在任何时候变成共享态 。相应地,如果产生了一个写请求,它就可以在任何时候变成修改态 。
    共享态(Shared),意味着该 Cache Line 可能在多个 Cache 中都有备份,并且是相同的状态,它是和内存内容保持一致的一份拷贝,而且可以在任何时候都变成其他三种状态 。
    失效态(Invalid),该 Cache Line 要么已经不在 Cache 中,要么它的内容已经过时 。一旦某个Cache Line 被标记为失效,那它就被当作从来没被加载到 Cache 中 。
    总得来说,若干个CPU核心通过Ringbus连到一起 。每个核心都维护自己的Cache的状态 。如果对于同一份内存数据在多个核里都有Cache,则状态都为S(Shared) 。
    一旦有一核心改了这个数据(状态变成了M),其他核心就能瞬间通过Ringbus感知到这个修改,从而把自己的Cache状态变成I(Invalid),并且从标记为M的Cache中读过来 。同时,这个数据会被原子的写回到主存 。最终,Cache的状态又会变为S 。
    关于MESI协议更详细的信息就不在本文中介绍了,在计算机操作系统和体系结构相关书籍和资料中有更详细的介绍 。
    然后compxchg这个指令就很简单了,和我们之前提到的一样,比较两个地址中的值是否相等,如果相等的话那么就修改 。
    Interlocked类中的其它方法也是同样的原理,我们可以看看Add之类的方法,同样是在对应的操作指令前加了lock指令 。
    从ObjectPool到CAS指令

    文章插图
    总结本文主要是带大家看了下ObjectPool的源码,然后看了看ObjectPool能实现无锁线程安全的最大功臣Interlocked.CompareExchange方法;然后通过汇编代码了解了一下Interlocked类中的一些方法是如何做到原子性的 。
    感谢阅读,如果您觉得本文还不错,欢迎点赞、转发+评论,您的支持是我更新的动力!

    经验总结扩展阅读