缓存一致性

什么是缓存一致性问题?如何解决?

当程序在运行过程中,会将运算需要的数据从主存复制一份到 CPU 的高速缓存中。那么 CPU 进行计算时 就可以直接从它的高速缓存读取数据和向它写入数据,当运算结束后,再将高速缓存中的数据刷新到主存中。

举个简单的例子,比如下面的一行代码:

i = i + 1;

当线程执行这条语句时,会先从主存中读取变量 i 的值,然后复制一份到高速缓存中,然后CPU执行指令 对 i 进行加 1 操作,然后将数据写入到高速缓存,最后将高速缓存中 i 最新的值刷新到主存中。

在单线程中运行代码中是正常的,但在多线程中会出现问题。在多核 CPU 中,每个线程可能运行于 不同的 CPU 中,因此每个线程运行时都有自己的高速缓存(对于单核 CPU,其实也会出现这种问题, 只不过是以线程调度的形式分别执行的),此处以多核 CPU 进行阐述。

比如同时有 2 个线程执行这段代码,假如初始时 i 的值为 0,那么我们希望两个线程执行完之后 i 的值 变为 2,但是事实如此吗?

可能存在下面一种情况:初始时,两个线程分别读取 i 的值存入各自所在的 CPU 的高速缓存中, 然后线程 1 进行加 1 操作,然后把 i 的最新值 1 写入到内存。此时线程的高速缓存中 i 的值 仍然是 0,进行加 1 操作之后,i 的值为 1,然后线程 2 把 i 的值写入内存。

最终,i 的值是 1,而不是 2。这就是著名的缓存一致性问题。 通常称这种被多个线程访问(修改)的变量为共享变量。

也即是说,如果一个变量在多个 CPU 中都存在缓存(一般在多线程编程是才会出现), 那么就可能存在缓存不一致的问题。

为了解决缓存不一致的问题,通常有如下两种解决方法:

(1) 通过在总线加锁的方法

(2) 通过缓存一致性协议

在早期的 CPU 中,是通过在总线上加锁的形式解决缓存不一致的问题。因为 CPU 和其它部件都是通过 总线进行通信的,如果对总线进行加锁,也就阻塞了其它 CPU 对其它部件访问共享的内容,如内存, 从而使得只能有一个 CPU 能使用这个变量的内存。比如上面例子中,如果一个线程在执行 i = i + 1, 如果在执行这行代码的过程中,在总线上发出了 LOCK 锁的信号,那么只有等待这段代码完全执行完毕之后, 其他 CPU 才能从变量 i 所在的内存读取变量,然后进行相应的操作。这样就解决了缓存不一致的问题。

但是上面的方式会有一个问题,由于在锁住总线期间,其他 CPU 无法访问内存,导致效率低下。

所以就出现了缓存一致性协议。该协议保证了每个缓存中使用的共享变量的副本是一致的。 它的核心思想是,当 CPU 想内存写入数据时,如果发现操作的变量是共享变量,即在其他 CPU 中 也存在该变量的副本,则会发出信号通知其他 CPU 将该变量的缓存行设置为无效状态,因此当其他 CPU 需要读取这个变量时,发现自己缓存中的该变量的缓存行是无效的,那么它就会从主内存中重新读取。

results matching ""

    No results matching ""