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


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

文章插图
 
当程序在运行过程中 , 会将运算需要的数据从主存复制一份到CPU高速缓存中 , 那么CPU进行计算时就可以从它的高速缓存读取数据和向其中写入数据 , 当运算结束后 , 再将高速缓存中的数据刷新到主存当中 。举个简单的例子 , 比如下面的这段代码:
当线程执行这个语句时 , 会先从主存当中读取i的值 , 然后复制一份到高速缓存当中 , 然后CPU执行指令对i指令进行加1操作 , 然后将数据写入高速缓存 , 最后将高速缓存中i最新的值刷新到主存当中 。
这个代码在单线程中运行时没有任何问题的 , 但是在多线程中运行就会有问题了 。在多核CPU中 , 每条线程可能运行于不同的CPU中 , 因此每个线程运行时有自己的高速缓存(对单核CPU来说 , 其实也会出现这种问题 , 只不过是以线程调度的形式来分别执行的) 。我们以多核CPU为例 。
比如同时有两个线程执行这段代码 , 假如初始时i的值为0 , 那么我们希望两个线程执行完之后i的值变为2 。但事实会是这样吗?
可能存在下面一种情况:初始时 , 两个线程分别读取i的值存入各自所在的CPU的高速缓存当中 , 然后线程1进行加1操作 , 然后把i的最新值1写入到内存 。此时线程2的高速缓存当中i的值还是0 , 进行加1操作后 , i的值为1 , 然后线程2把i的值写入内存 。
最终结果i的值是1 , 而不是2 。这就是著名的缓存一致性问题 。通常称这种被多个线程访问的变量为共享变量 。
也就是说 , 如果一个变量在多个CPU中都存在缓存(一般在多线程编程时才会出现) , 那么就可能存在缓存不一致的问题 。
为了解决缓存不一致问题 , 通常来说有以下2种解决方法:
【什么是缓存一致性问题?如何解决呢?】1)通过在总线加LOCK , 锁的方式;
2)通过缓存一致性协议;
在早期的CPU中 , 是通过在总线上加LOCK锁的形式来解决缓存不一致的问题 。因为CPU和其他部件进行通信都是通过总线来进行的 , 如果对总线加LOCK锁的话 , 也就是说阻塞了其他CPU对其它部件访问(如内存) , 从而使得只能有一个CPU能使用这个变量的内存 。比如上面例子中 , 如果一个线程在执行i = i +1 , 如果在执行这段代码的过程中 , 在总线上发出了LOCK锁的信号 , 那么只有等待这段代码完全执行完毕之后 , 其他CPU才能从变量i所在的内存读取变量 , 然后进行相应的操作 。这样就解决了缓存不一致的问题 。
但是上面的方式会有一个问题 , 由于在锁住总线期间 , 其他CPU无法访问内存 , 导致效率低下 。
所以就出现了缓存一致性协议 。该协议保证了每个缓存中使用的共享变量的副本是一致的 。它的核心思想是:当CPU向内存写入数据时 , 如果发现操作的变量是共享变量 , 即在其他CPU中也存在该变量的副本 , 会发出信号通知其他CPU将该变量的缓存行置为无效状态 , 因此当其他CPU需要读取这个变量时 , 发现自己缓存中缓存该变量的缓存是无效的 , 那么它就会从内存重新读取 。




    推荐阅读