为什么会有写缓冲器 ,无效化队列 ?
先来看一下什么是缓存一致性协议
核心思想:如果变量是共享变量,当其他CPU修改这个变量的时候,发出信号将其他CPU的变量置为失效状态,其他CPU再使用这一变量的时候会从内存中加载这一变量。
修改共享数据的时候,发现相应 缓存行的状态是S, 就是共享,说明其他cpu也有副本, 这时候要发 无效 的通知出去广播给其他cpu把缓存里面的值为无效(Invalid)状态,然后待收到各个缓存的(Invalid ack)已经完成无效状态修改的回应之后,再把自己的状态改为Exclusive,之后再进行修改,修改后再改为Modified状态,数据写入缓存行。
这样会有一个缺点,CPU需要在等待所有的Invalid ack之后才会进行下面的操作。这会让CPU产生一定的阻塞,无法充分利用CPU。这个时候就出来了写缓冲器和无效队列化。
写缓冲器
写的时候,发现相应 缓存行的状态是S, 就是共享,说明其他cpu也有副本, 这时候要发 无效 的通知出去广播给其他cpu把缓存里面的值为无效(Invalid)状态 本来是要等回复才能继续的, 有了
写缓冲器
就可以先把修改的数据放到写缓冲器,然后通知等其他cpu等他们回复(ack)
, 这个时候不会造成线程阻塞,cpu可以利用等待的时间继续向下执行,等回复都到了以后cpu再来把数据从写缓冲器
搬运到缓存行。
存储转发
cpu读取数据的时候先从 写缓存器
读, 没有 再去 读高速缓存
, 这样就可以还是读到真的数据了无效化队列
其他cpu收到无效通知以后, 并不把数据无效化, 而是存入无效化队列
就回复 收到无效Ack,这样可以让对方早点收到回复,等cpu忙完当前的事在进行读取无效化队列。
这样会造成一些问题
- 存储转发:cpu0 更新了a的值, 写到
写缓冲器
就往下走了,过一阵,要读a,这时先去写缓存器
读, 读到的自以为是最新的,但是没准这一阵时间里面, cpu1已经改过a的值了,但是, cpu1发过来的无效通知, 是管不到 cpu0的写缓冲器
的。 - 写缓冲器:其他CPU还没有给我们回答的时候我们已经执行下一步代码了。
- 无效化队列:其他CPU已经给对方应答的时候自己本身还没有去把这个值改为无效状态,这样就造成当前变量已经无效,但是通知还在无效队列化中,会取到旧值。
存储屏障 加载屏障
总之, 写缓冲器 ,无效化队列 就是导致了可见性问题, 明明写了 其他线程看不到这就需要编译器等底层系统 借助 内存屏障
存储屏障 : 让cpu 将 写缓冲器
排空,写入高速缓存
这叫冲刷
, 这样其他cpu 就会收到通知, 其他cpu可以来拿新数据
加载屏障:cpu 根据无效化队列
里面的信息,删除其高速缓存
的无效数据(就是状态变为I)
这2个屏障的成对使用, 才能保证更新可见