多线程并发艺术(二)volatitle修饰符之缓存一致性协议

缓存一致性协议

  1. 在讨论volatitle修饰符之前,我们需要先去了解缓存一致性协议。
  2. 我认为在学习一门知识的时候,你需要清晰的知道,你学习的是什么,它是用于干什么的,以及怎样运用它。对于网上的博客中的内容更不应该无理由的相信。
  3. 比如本章学习的是volatitle修饰符,那即将了解的是缓存一致性协议。在了解缓存一致性协议之前,你应该想,volatitle是运用于java的一种实现JMM可见性有序性的修饰符,为什么需要先了解缓存一致性协议,缓存一致性协议是什么,它又与volatitle有什么关系等等。
  4. 诸如此类的自我询问,有助于你更快的掌握一门新的知识。
  5. 当然,这只是我的一种建议,读者参考即可。

  1. 什么是缓存一致性协议呢,缓存一致性协议是为了处理多个处理器处理同一个主存地址问题而产生的。其中MESI是一种主流的缓存一致性协议,是一种广泛使用的支持写回策略的缓存一致性协议。

在这里插入图片描述
在看到上述图的时候,不知道大家有没有想起前面Java内存模型的图,是不是感觉二者一模一样。
一个主要的cpu运行计算的步骤如下:

1)程序以及数据被载入到主内存
2)指令和数据被载入到cpu的高速缓存
3)cpu运行处理时,将结果写进高速缓存区
4)高速缓存区中的数据写会主内存
  1. 理解了这个我们再看一下什么是

MESI(Modified Exclusive Shared Or Invalid)

它定义了缓存行的几种状态

1) modified(改动) :该缓存行只被缓存在该CPU的缓存中,并且是被修改过的,即与主内存中的数据不一致。该缓存行中的内存需要在未来的某个时间点(允许其他CPU读取主存中相应内存之前)写回主存。当被写回主存之后,该缓存行的状态会变成独享状态(exclusive)。
2)exclusive(独享):该缓存航只被缓存在该CPU的缓存中,它是未被修改的,与主存中数据一致。该状态可以在任何时刻当有其他CPU读取该内存时变成共享状态(shred),同样的,当CPU修改该缓存行中内容时候,该状态可以变回Modified状态。
3)share(共享):该状态意味着该缓存行可能被多个CPU缓存,并且各个缓存中的数据与主存数据一致,当有一个CPU修改该缓存行时,其他CPU中该缓存行作废,变成无效状态(invalid)
4)invalid(无效):缓存行中的数据无效

操作如下:
1)一个读取的请求,除了缓存行在Invalid都可以满足。一个invalid状态的缓存行必须从主存中读取(变成S或者M状态)来满足该CPU的读请求
2)一个写请求只有在该缓存行是M 或者 E 状态时候才可以被执行,如果缓存处于S状态,必须先把其他缓存中该缓存行变成Invalid状态,不允许不同CPU同一时刻修改用一缓存行,即使是修改该缓存行中不同位置的数据也不允许。该操作通常用广播的方式来完成
3)对于M和E状态的缓存行是精确的,他们和该缓存行的真正状态是一致的。而S状态可能是非一致的。如果一个缓存将处于S状态的缓存行作废了,而另一个缓存行可能实际上已经独享了该缓存行,但是CPU缓存并不会将该缓存行的状态并升迁为E状态(独享),这是因为其他缓存不会广播它们作废了该缓存行的通知,同样由于缓存并没有保存该缓存行的copy数量(即有多少cpu缓存了该缓存行),因此也没有办法确定自己是否已经独享了该缓存行。
4)一个处于S状态的缓存行也必须监听其他CPU缓存使自己的缓存行无效或者独享该缓存行的请求,并将该缓存行变成无效。
5)一个处于E状态的缓存行也必须监听其他缓存读取主存中该缓存行的操作,一旦有这种操作,该缓存行需要变成S状态。
6)一个处于M状态的缓存行必须监听所有来自其他CPU缓存,关于该缓存行内容的读取请求,这种操作必须延迟到该持有M状态的缓存行的CPU将该M缓存行写回主内存并将状态变成S之后,执行。
7)从上面的意义看来,E状态是一种投机性的优化,如果一个CPU想修改一个处于S状态的缓存(把它变成E或者M),总线事务需要将所有该缓存行的copy变成invalid状态,但是修改E状态的缓存不需要使用总线事务。

可参考英文原文:https://en.wikipedia.org/wiki/MESI_protocol


在我上文中有标记两端文字,当时我在观看英文原文时,不太理解,英文原文分别如下:

Other caches do not broadcast notices when they discard cache lines
其他缓存不会广播它们作废了该缓存行的通知

A cache that holds a line in the Shared state must listen for invalidate or request-for-ownership broadcasts from other caches, and discard the line (by moving it into Invalid state) on a match.
一个处于S状态的缓存行也必须监听来自其他缓存中缓存行无效或者独享该缓存行的请求,并将该缓存行变成无效。

  1. 这里的 listen for invalidate可能有两个意思
  • 监听到了其他CPU缓存将它们的该缓存行状态改为invalid
  • 监听到了其他CPU缓存请求将该你的该缓存行状态改为invalid
  1. 联系到后面的*discard the line (by moving it into Invalid state) on a match.*我认为还是第二种意思是正确的,而且也契合了原文中的
    其他缓存不会广播它们作废了该缓存行的通知 的这一意思。

如果有读者持不同意见的,欢迎留言讨论。


用文字述说很明显是极度僵硬的,下面我用一串图来展示MESI快速缓存一致性协议的状态转换实例
在这里插入图片描述

步骤一:处理器A读取了主内存中的缓存块a,并且写入了自己的缓存区中。并将该缓存块a修改转改为Exclusive(独享状态)
步骤二:处理器B这个时候也读取了主内存中的缓存块a,它发现了a的状态为Exclusive,于是将a的状态改为了Share(共享状态),处理器A中的该状态行也监听到了这一行为,并将状态修改为了Share
步骤三:处理器A这个时候需要修改a,发现它的状态为Share,于是将它的状态修改为了Modified,并广播提示其他cpu中该缓存行的状态从Share改成Invalid,于是其他CPU缓存区中a的状态都变成了Invalid(无效状态)
步骤四:处理器B这个时候需要区读取a,发现了a的状态为Invalid,是无效的,那它就去主内存中去再次读取a。这一请求被处理器A中的Modified状态a监听到了,于是它先让处理器B的这次读取延迟,直到处理器A将自身缓存区中a的值写回主存中,并且将a的状态修改为Share之后,执行。这个时候处理器B也就很成功的从主内存中读取到了a,并且a的状态为Share(共享状态)


很明显缓存一致性协议最大的问题就是可能引起一致性流量风暴。之前我们看到总线在同一时刻仅仅只能被一个处理器使用。当有大量缓存被改动时候,或者同一个缓存块一直被改动时,会产生大量的缓存一致性流量。从而占用了总线。影响了其他正常的读写请求。

一个最常见的样例就是假设多个线程对同一个变量一直使用CAS操作。那么会有大量改动操作。从而产生大量的缓存一致性流量,由于每一次CAS操作都会发出广播通知其他处理器,从而影响程序的性能。

这些问题后续我也会自行在网上翻阅并整理出对应的博客,以及对于CAS操作的介绍,并进行发布。


本博客部分内容参考了
https://www.cnblogs.com/mfrbuaa/p/5068812.html

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

范大

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值