MESI缓存一致性协议,用于解决多线程环境下的缓存一致性问题。
在多处理器系统中,每个处理器都有自己的高速缓存,而它们又共享同一主内存。当多个处理器的运算任务都涉及同一块主内存区域时,将可能导致各自的缓存数据不一致的情况,如果真的发生这种情况,那同步回主内存时以谁的缓存数据为准呢?!这个就是缓存一致性问题。
为了解决一致性的问题,需要各个处理器访问缓存时都遵循一些协议,在读写时要根据协议来进行操作。主要使用的协议是:MESI缓存一致性协议。
早期的计算机使用的是【总线加锁】的方式,但是这种方式性能太低,现在使用的基本都是【缓存一致性协议】,但是总线加锁也是有的,某些情况下会用总线加锁方式。
原子性,可见性,有序性是并发编程中三个重要的概念。
缓存一致性协议不是万能的,它只是解决了可见性问题而已,没法解决有序性和原子性问题。
了解缓存一致性协议之前,先了解下缓存行(Cache line)。
CPU缓存(cache)是由很多个Cache line组成的。Cache line是CPU缓存和主存交换数据的最小单位,Cache line大小是固定的,通常为64Byte。
当数据大小超过Cache line的固定大小时,就没法用缓存一致性协议了,会转而使用总线加锁。
当从内存中取数据到cache中时,会一次取一个Cache line大小的内存区域到cache中,然后存进相应的Cache line中。
作用:解决多线程环境下的缓存一致性问题。
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无效。 | 无 |
工作原理:
如果两个CPU同一时间要去修改X会怎样?
一个指令周期内会进行裁决,只能有一个修改成功!
如果裁决失败,整个缓存行(Cache line)会整体失效。
那么又有一个很重要的问题,缓存一致性协议什么时候触发呢?
首先,缓存一致性协议需要硬件的支持,但是这个问题不用考虑,因为现在的硬件已经是支持的。只不过该协议是弱一致,正常情况下,系统操作并不会进行缓存一致性(MESI)的校验。
要触发缓存一致性协议,需要在汇编指令中添加 #Lock信号!
但是我们平时编程中,显然不可能去手动写什么 #Lock信号。对于java来说,就是volatile关键字,因为volatile关键字可以触发#Lock信号,从而能触发缓存一致性协议。当然,volatile关键字还不止这一个功能,它还可以一定程度上禁止指令重排序!也就是说,volatile关键字可以同时解决可见性问题以及有序性问题,不过不能解决原子性问题!