public override void Return(T obj){// 使用策略的Return方法对元素进行处理// 比如 List<T> 需要调用Claer方法清除集合内元素// StringBuilder之类的也需要调用Claer方法清除缓存的字符if (_isDefaultPolicy || (_fastPolicy?.Return(obj) ?? _policy.Return(obj))){// 先尝试将归还的元素赋值到 _firstItem中if (_firstItem != null || Interlocked.CompareExchange(ref _firstItem, obj, null) != null){var items = _items;// 如果 _firstItem已经存在元素// 那么遍历整个数组空间 找一个存储为null的空位将对象存储起来for (var i = 0; i < items.Length && Interlocked.CompareExchange(ref items[i].Element, obj, null) != null; ++i){}}}}
从核心的Get()
和Set()
方法来看,其实整个代码是比较简单的,除了有一个_firstItem
有一个简单的优化 , 其余没有什么特别的复杂的逻辑 。
主要的关键就在Interlocked.CompareExchange
方法上,我们在下文来仔细研究一下这个方法 。
关于 Interlocked.CompareExchangeInterlocked.CompareExchange
它实际上是一个CAS
的实现,也就是Compare And Swap,从名字就可以看出来,它就是比较然后交换的意思 。
从下面的代码段我们也可以看出来,它总共需要三个参数 。其特性就是只有当localtion1 == comparand
的时候才会将value
赋值给localtion1
, 另外吧localtion1
的原始值返回出来,这些操作都是原子性的 。
// localtion1 需要比较的引用A// value 计划给引用A 赋的值// comparand 和引用A比较的引用public static T CompareExchange<T> (ref T location1, T value, T comparand)where T : class;
一个简单的流程如下所示:

文章插图
简单的使用代码如下所示:
var a = 1;// a == 1的话就将其置为0// 判断是否成功就看返回的值是否为a的原始值if(Interlocked.CompareExchange(ref a, 0, 1) == 1) Console.WriteLine("1.成功");// 现在a已经变为0 这个交换不会成功if(Interlocked.CompareExchange(ref a, 0, 1) == 1) Console.WriteLine("2.成功");
结果如下所示,只有当a
的原始值为1
的时候,才会交换成功:
文章插图
那么
Interlocked.CompareExchange
是如何做到原子性的?在多核CPU中,数据可能在内存或者L1、L2、L3中(如下图所示),我们如何保证能原子性的对某个数据进行操作?
文章插图
实际上这是CPU提供的功能,如果查看过JIT编译的结果,可以看到
CompareExchange
是由一条叫lock cmpxchgl
的汇编指令支撑的 。
文章插图
其中
lock
是一个指令前缀,汇编指令被lock
修饰后会成为"原子的",lock
指令有两种实现方法:- 早期 - Pentium时代(锁总线),在Pentium及之前的处理器中,带有
lock
前缀的指令在执行期间会锁住总线,使得其它处理器暂时无法通过总线访问内存,很显然,这个开销很大 。 - 现在 - P6以后时代(锁缓存),在新的处理器中,Intel使用缓存锁定来保证指令执行的原子性,缓存锁定将大大降低lock前缀指令的执行开销 。
MESI
协议是 Cacheline 四种状态的首字母的缩写,分别是修改(Modified)态、独占(Exclusive)态、共享(Shared)态和失效(Invalid)态 。Cache 中缓存的每个 Cache Line 都必须是这四种状态中的一种 。修改态(Modified) , 如果该 Cache Line 在多个 Cache 中都有备份,那么只有一个备份能处于这种状态,并且“dirty”标志位被置上 。拥有修改态 Cache Line 的 Cache 需要在某个合适的时候把该 Cache Line 写回到内存中 。但是在写回之前,任何处理器对该 Cache Line在内存中相对应的内存块都不能进行读操作 。Cache Line 被写回到内存中之后,其状态就由修改态变为共享态 。
独占态(Exclusive),和修改状态一样 , 如果该 Cache Line 在多个 Cache 中都有备份,那么只有一个备份能处于这种状态,但是“dirty”标志位没有置上 , 因为它是和主内存内容保持一致的一份拷贝 。如果产生一个读请求,它就可以在任何时候变成共享态 。相应地,如果产生了一个写请求,它就可以在任何时候变成修改态 。推荐阅读
- 蜘蛛的丝是从哪里吐出来的?
- .net 温故知新:【8】.NET 中的配置从xml转向json
- Android10 dex2oat实践
- Spark简单介绍,Windows下安装Scala+Hadoop+Spark运行环境,集成到IDEA中
- 为什么剪指甲时不会感到疼?
- iqoo7电池多大_iqoo7电池多少毫安
- 12306购票卧铺如何订下铺(12306硬卧如何买到下铺)
- 12306怎么才能选到下铺(12306怎么买硬卧下铺)
- 12306如何订到下铺(打12306可以订到下铺吗)
- 微信好友删除了怎么找回(微信注销60天后警察还能查到吗)