MESI缓存一致性协议,用于解决多线程环境下的缓存一致性问题。
缓存一致性问题
在多处理器系统中,每个处理器都有自己的高速缓存,而它们又共享同一主内存。当多个处理器的运算任务都涉及同一块主内存区域时,将可能导致各自的缓存数据不一致的情况,如果真的发生这种情况,那同步回主内存时以谁的缓存数据为准呢?!这个就是缓存一致性问题。
为了解决一致性的问题,需要各个处理器访问缓存时都遵循一些协议,在读写时要根据协议来进行操作。主要使用的协议是:MESI缓存一致性协议。
早期的计算机使用的是【总线加锁】的方式,但是这种方式性能太低,现在使用的基本都是【缓存一致性协议】,但是总线加锁也是有的,某些情况下会用总线加锁方式。
原子性 可见性 有序性
原子性,可见性,有序性是并发编程中三个重要的概念。
- 可见性:指的是当一个线程修改了某个共享变量的值,其他线程是否能够马上得知这个修改的值。
- 有序性:指的是对于单线程的执行代码,我们总是认为代码的执行是按顺序依次执行的(程序编译成机器码指令后可能会出现指令重排现象,重排后的指令与原指令的顺序未必一致)。
- 原子性:指的是一个操作是不可中断的,即使是在多线程环境下,一个操作一旦开始就不会被其他线程影响。
缓存一致性协议不是万能的,它只是解决了可见性问题而已,没法解决有序性和原子性问题。
缓存行(Cache line)
了解缓存一致性协议之前,先了解下缓存行(Cache line)。
CPU缓存(cache)是由很多个Cache line组成的。Cache line是CPU缓存和主存交换数据的最小单位,Cache line大小是固定的,通常为64Byte。
当数据大小超过Cache line的固定大小时,就没法用缓存一致性协议了,会转而使用总线加锁。
当从内存中取数据到cache中时,会一次取一个Cache line大小的内存区域到cache中,然后存进相应的Cache line中。
MESI缓存一致性协议
作用:解决多线程环境下的缓存一致性问题。
MESI是4种状态的首字母:Modified、Exclusive、Shared、Invalid。
状态 | 描述 | 监听任务 |
---|---|---|
M 修改(Modified) |
该Cache line有效,数据被修改了,和内存中的数据不一致,数据只存在于本Cache中。 | 缓存行必须时刻监听所有试图读该缓存行相对就主存的操作,这种操作必须在缓存将该缓存行写回主存并将状态变成S(共享)状态之前被延迟执行。 |
E 独享、互斥(Exclusive) |
该Cache line有效,数据和内存中的数据一致,数据只存在于本Cache中。 | 缓存行也必须监听其它缓存读主存中该缓存行的操作,一旦有这种操作,该缓存行需要变成S(共享)状态。 |
S 共享 (Shared) |
该Cache line有效,数据和内存中的数据一致,数据存在于很多Cache中。 | 缓存行也必须监听其它缓存使该缓存行无效或者独享该缓存行的请求,并将本缓存行变成 I(无效)状态。 |
I 无效 (Invalid) |
该Cache line无效。 | 无 |
工作原理:
- 变量X=1读到CPU1的缓存中,标记为独占状态(E),此CPU会监听(总线嗅探机制)其他CPU对这块内存的操作
- CPU2也从内存中讲X=1读到缓存中,这个时候CPU1通过总线嗅探机制会知道这个操作,这时会把X的状态改成共享状态(S),当然,CPU2中X也是共享状态
- CPU1中把X改成2,准备回写到主存中,CPU1中会锁住这个缓存行,然后把X状态改成修改状态(M),然后往总线发一个消息
- 其他CPU此时嗅探到这个消息,如果此CPU上也有X的缓存的话,会修改为无效状态(I)
- CPU1成功将X=2回写到主存后,会把X修改为独占状态(E)
- 其他CPU此时需要再访问X的话,发现是无效状态,则需要从主从中重新去读取
如果两个CPU同一时间要去修改X会怎样?
一个指令周期内会进行裁决,只能有一个修改成功!
如果裁决失败,整个缓存行(Cache line)会整体失效。
缓存一致性协议什么时候触发
那么又有一个很重要的问题,缓存一致性协议什么时候触发呢?
首先,缓存一致性协议需要硬件的支持,但是这个问题不用考虑,因为现在的硬件已经是支持的。只不过该协议是弱一致,正常情况下,系统操作并不会进行缓存一致性(MESI)的校验。
要触发缓存一致性协议,需要在汇编指令中添加 #Lock信号!
但是我们平时编程中,显然不可能去手动写什么 #Lock信号。对于java来说,就是volatile关键字,因为volatile关键字可以触发#Lock信号,从而能触发缓存一致性协议。当然,volatile关键字还不止这一个功能,它还可以一定程度上禁止指令重排序!也就是说,volatile关键字可以同时解决可见性问题以及有序性问题,不过不能解决原子性问题!
什么时候缓存一致性协议会失效
- 如果X存储长度超过一个缓存行,会使用总线加锁。不能一个数据横跨多个缓存行(数据可以横跨多个缓存行,只是缓存一致性协议会失效)
- CPU并不支持缓存一致性协议。当然,这肯定是早期的CPU