【杂谈】高速缓存一致性与可见性

这里的缓存指的是什么?

到处都可以有缓存,磁盘中的数据提前加载到内存中就是常见的“缓存”,而我们这里要说的是内存中的数据提前加载到CPU的缓存。每个CPU都有一定的缓存空间。

什么样的数据会存入缓存?

编译器或CPU可以明确知晓的,可能被经常访问的数据。例如一个在循环体中的变量,因为这个变量在循环结束前需要经常访问,如果每次都从主存中拿,那就太慢了。

缓存一致,是跟谁一致?

是跟主存一致,当主存中的对应数据发生变动的时候,CPU中的缓存也会随之变动。例如Cache中缓存了变量x的值,当主存中的x的值变动的时候,Cache中的值也会刷新。“刷新”可能是直接用新值覆盖,也可能是将旧值标记为“失效”,下次需要时从主存拉取。

主存的数据变动是如何被监听到的?

Bus Snooping。总线窥探,只要监听总线操作就可知道其他CPU对主存的访问情况。例如监听到x变量的写操作,那就表明x变量的值有变动。

高速缓存一致这些操作谁来实现?

底层硬件实现,现代计算机都有提供此功能。所以,对于高速缓存一致性,我们应用层的代码做不到,也不需要我们来做。

那这样是不是已经有了可见性?

没有,当然如果可见性指的是“当主存值发生变化的时候,Cache可以看到”,那确实是实现了。问题是可见性不是这么定义的,它说的是“当一个线程对一个变量进行修改时,其他线程都能够看到”。

这两种表述方式有什么不同?

其实就是“一个线程对变量A的修改” ≠ “主存值发生变化”。也就说,修改变量的值后,修改的只是缓存中的值,不会马上写入主存。

为什么不马上写入主存?

因为慢。所以这个写操作会被重排序到后面,这个操作还是会执行的,只是优先级没那么高。

那是不是马上写入主存就能实现可见性呢?

是的,只要马上写入主存,由于底层提供“高速缓存一致性”,所以当内存中的变量发生变更时,其他CPU的缓存也会随之更新。那这样可见性就有了。

那么如何让它马上写入主存呢?

防止重排序就可以了,这样写入内存的操作就会被立即执行。一般就是加内存屏障。synchronized、volatile都依赖内存屏障。

什么时候需要可见性?

正常的程序变量一般都不需要可见性。除非这个变量可能被多个线程同时访问,且你需要用这个变量来协调线程操作。那这时候这个变量才需要具有可见性。这时候如果不保证可见性,就很可能出现奇怪的问题,即有时可以正确执行,有时又不行。为什么呢?因为有时候,这些线程刚好在同一CPU上执行,访问的是同一个Cache,自然就能得到正确的,也就是最新的值。但是,大多数情况不是这样,多个线程会在多个CPU上执行,如果不保证可见性,就可能得到过期的值。然后你就会很奇怪,明明看日志打印,已经改了啊,为什么其他线程还是没反应过来呢?

插一句

其实缓存一致性和可见性的问题,都是由多CPU的引发的,就是因为每个CPU都有一个Cache,所以才有了这一堆的问题。

posted @ 2019-05-11 15:55  猫毛·波拿巴  阅读(850)  评论(1编辑  收藏  举报