《编程篇》已经涉及到了对象池模型的大部分核心接口和类型。对象池模型其实是很简单的,不过其中有一些为了提升性能而刻意为之的实现细节倒是值得我们关注。总的来说,对象池模型由三个核心对象构成,它们分别是表示对象池的ObjectPool<T>对象、对象值提供者的ObjectPoolProvider对象,已及控制池化对象创建与释放行为的IPooledObjectPolicy<T>对象,我们先来介绍最后一个对象。
一、 IPooledObjectPolicy<T>我们在《编程篇》已经说过,表示池化对象策略的IPooledObjectPolicy<T>对象不仅仅帮助我们创建对象,还可以帮助我们执行一些对象回归对象池之前所需的回收操作,对象最终能否回到对象池中也受它的控制。如下面的代码片段所示,IPooledObjectPolicy<T>接口定义了两个方法,Create方法用来创建池化对象,对象回归前需要执行的操作体现在Return方法上,该方法的返回值决定了指定的对象是否应该回归对象池。抽象类PooledObjectPolicy<T>实现了该接口,我们一般将它作为自定义策略类型的基类。
public interface IPooledObjectPolicy<T> { T Create(); bool Return(T obj); } public abstract class PooledObjectPolicy<T> : IPooledObjectPolicy<T> { protected PooledObjectPolicy(){} public abstract T Create(); public abstract bool Return(T obj); }
我们默认使用的是如下这个DefaultPooledObjectPolicy<T>类型,由于它直接通过反射来创建池化对象,所以要求泛型参数T必须有一个公共的默认无参构造函数。它的Return方法直接返回True,意味着提供的对象可以被无限制地复用。
public class DefaultPooledObjectPolicy<T> : PooledObjectPolicy<T> where T: class, new() { public override T Create() => Activator.CreateInstance<T>(); public override bool Return(T obj) => true; }
二、ObjectPool<T>对象池通过ObjectPool<T>对象表示。如下面的代码片段所示,ObjectPool<T>是一个抽象类,池化对象通过Get方法提供给我们,我们在使用完之后调用Return方法将其释放到对象池中以供后续复用。
public abstract class ObjectPool<T> where T: class { protected ObjectPool(){} public abstract T Get(); public abstract void Return(T obj); }
DefaultObjectPool<T>我们默认使用的对象池体现为一个DefaultObjectPool<T>对象,由于针对对象池的绝大部分实现就体现这个类型中,所以它也是本节重点讲述的内容。我们在前面一节已经说过,对象池具有固定的大小,并且默认的大小为处理器个数的2倍。我们假设对象池的大小为N,那么DefaultObjectPool<T>对象会如下图所示的方式使用一个单一对象和一个长度为N-1的数组来存放由它提供的N个对象。
如下面的代码片段所示,DefaultObjectPool<T>使用字段_firstItem用来存放第一个池化对象,余下的则存放在_items字段表示的数组中。值得注意的是,这个数组的元素类型并非池化对象的类型T,而是一个封装了池化对象的结构体ObjectWrapper。如果该数组元素类型改为引用类型T,那么当我们对某个元素进行复制的时候,运行时会进行类型校验(要求指定对象类型派生于T),无形之中带来了一定的性能损失(值类型数组就不需求进行派生类型的校验)。我们在前面提到过,对象池中存在一些性能优化的细节,这就是其中之一。
public class DefaultObjectPool<T> : ObjectPool<T> where T : class { private protected T _firstItem; private protected readonly ObjectWrapper[] _items; … private protected struct ObjectWrapper { public T Element; } }
DefaultObjectPool<T>类型定义了如下两个构造函数。我们在创建一个DefaultObjectPool<T>对象的时候会提供一个IPooledObjectPolicy<T>对象并指定对象池的大小。对象池的大小默认设置为处理器数量的2倍体现在第一个构造函数重载中。如果指定的是一个DefaultPooledObjectPolicy<T>对象,表示默认池化对象策略的_isDefaultPolicy字段被设置成True。因为DefaultPooledObjectPolicy<T>对象的Return方法总是返回True,并且没有任何具体的操作,所以在将对象释放回对象池的时候就不需要调用Return方法了,这是第二个性能优化的细节。
public class DefaultObjectPool<T> : ObjectPool<T> where T : class { private protected T _firstItem; private protected readonly ObjectWrapper[] _items; private protected readonly IPooledObjectPolicy<T> _policy; private protected readonly bool _isDefaultPolicy; private protected readonly PooledObjectPolicy<T> _fastPolicy; public DefaultObjectPool(IPooledObjectPolicy<T> policy) : this(policy, Environment.ProcessorCount * 2) {} public DefaultObjectPool(IPooledObjectPolicy<T> policy, int maximumRetained) { _policy = policy ; _fastPolicy = policy as PooledObjectPolicy<T>; _isDefaultPolicy = IsDefaultPolicy(); _items = new ObjectWrapper[maximumRetained - 1]; bool IsDefaultPolicy() { var type = policy.GetType(); return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(DefaultPooledObjectPolicy<>); } } [MethodImpl(MethodImplOptions.NoInlining)] private T Create() => _fastPolicy?.Create() ?? _policy.Create(); }