本文介绍本人的一个简单Object Pool实现。什么是Object Pool呢?大家可能都知道什么是数据库连接池,他能极大避免不需要的对象销毁和初始化开销。本文实现的对象池是一个通用的可用于任何有实例化接口的对象的池。默认的对象的实例化接口是new,文中也演示了如果您的对象需要从一个Factory构造,或当你的对象是用Emit生成时,如何简单继承ObjectPool类,实现特殊的对象的池化操作。
主要的类
ObjectPool - 池对象
PoolableAttribute - Poolable属性,可以用于修饰你定义的类,设置池化时的初始参数
SyncGuard - 用于保证线程安全的锁对象,保证ObjectPool可以被多线程安全共享
PoolableAttribute - Poolable属性
我们知道,一个池需要很多基本的参数,包括池的最大最小值等,本类就是用于辅助设置这些信息的。当然,该类和这些属性参数并不是必须的,如果您不给一个类设置该参数,那么,Pool将是用默认的参数值。
该类将允许您设置一下这些参数:
MinPoolSize
MaxPoolSize
PreferedPoolSize //该值表示,当Pool初始化时,希望Pool预先帮您实例化的对象数
CreationTimeout //该值表示,从Pool获取一个对象时的超时时间,一般如果不是内存吃紧,实例化对象本身不太会导致超时,因此,主要的超时原因可能会是因为Pool中的对象数已经达到允许的最大值,此时,从Pool中获取空闲对象的请求会要求等待一个对象空闲出来。因此,这个参数实际上主要用于后面这种超时情形。
using
System;
using
System.Collections.Generic;
using
System.Text;
namespace
Ilungasoft.Helper.ObjectPooling
{
[AttributeUsageAttribute(AttributeTargets.Class, AllowMultiple = false)]
public class PoolableAttribute : Attribute
{
Private Members#region Private Members
private int minPoolSize;
private int preferedPoolSize;
private int maxPoolSize;
private long creationTimeout;
/**//// <summary>
/// Checks the state.
/// </summary>
private void CheckState()
{
if (minPoolSize > preferedPoolSize || minPoolSize > maxPoolSize || preferedPoolSize > maxPoolSize)
{
throw new ArgumentException("The condition min <= prefered <= max pool size was not met");
}
if (creationTimeout < -1)
{
creationTimeout = InfiniteCreationTimeout;
}
}
#endregion
Const Members#region Const Members
public const int DefaultMinPoolSize = 1;
public const int DefaultPreferedPoolSize = 16;
public const int DefaultMaxPoolSize = 100000;
public const long DefaultCreationTimeout = 60000;
public const long MaxCreationTimeout = 0x80000000; // ~2 billions
public const long InfiniteCreationTimeout = -1;
#endregion
Public Members#region Public Members
/**//// <summary>
/// Initializes a new instance of the <see cref="T:PoolableAttribute"/> class.
/// </summary>
/// <param name="minPoolSize">Size of the min pool.</param>
/// <param name="preferedPoolSize">Size of the prefered pool.</param>
/// <param name="maxPoolSize">Size of the max pool.</param>
/// <param name="creationTimeout">The creation timeout.</param>
public PoolableAttribute(int minPoolSize, int preferedPoolSize, int maxPoolSize, long creationTimeout)
{
this.minPoolSize = minPoolSize;
this.preferedPoolSize = preferedPoolSize;
this.maxPoolSize = maxPoolSize;
this.creationTimeout = creationTimeout;
CheckState();
}
/**//// <summary>
/// Initializes a new instance of the <see cref="T:PoolableAttribute"/> class.
/// </summary>
/// <param name="minPoolSize">Size of the min pool.</param>
/// <param name="preferedPoolSize">Size of the prefered pool.</param>
/// <param name="maxPoolSize">Size of the max pool.</param>
public PoolableAttribute(int minPoolSize, int preferedPoolSize, int maxPoolSize) : this(minPoolSize, preferedPoolSize, maxPoolSize, DefaultCreationTimeout)
{
}
/**//// <summary>
/// Initializes a new instance of the <see cref="T:PoolableAttribute"/> class.
/// </summary>
public PoolableAttribute()
: this(DefaultMinPoolSize, DefaultPreferedPoolSize, DefaultMaxPoolSize, DefaultCreationTimeout)
{
}
#endregion
Properties#region Properties
/**//// <summary>
/// Gets or sets the size of the min pool.
/// </summary>
/// <value>The size of the min pool.</value>
public int MinPoolSize
{
get
{
return minPoolSize;
}
set
{
minPoolSize = value;
CheckState();
}
}
/**//// <summary>
/// Gets or sets the size of the max pool.
/// </summary>
/// <value>The size of the max pool.</value>
public int MaxPoolSize
{
get
{
return maxPoolSize;
}
set
{
maxPoolSize = value;
CheckState();
}
}
/**//// <summary>
/// Gets or sets the size of the prefered pool.
/// </summary>
/// <value>The size of the prefered pool.</value>
public int PreferedPoolSize
{
get
{
return preferedPoolSize;
}
set
{
preferedPoolSize = value;
CheckState();
}
}
/**//// <summary>
/// Gets or sets the creation timeout.
/// </summary>
/// <value>The creation timeout.</value>
public long CreationTimeout
{
get
{
return creationTimeout;
}
set
{
creationTimeout = value;
CheckState();
}
}
#endregion
}
}
SyncGuard - 用于保证线程安全的锁对象
这个类比较简单,主要就是一个锁,大家看看代码应该就能明白。
using
System;
using
System.Collections.Generic;
using
System.Text;
using
System.Threading;
namespace
Ilungasoft.Helper.ObjectPooling
{
internal class SyncGuard
{
Private Members#region Private Members
private object target;
private bool locked;
#endregion
Constructors#region Constructors
/**//// <summary>
/// Initializes a new instance of the <see cref="T:SyncGuard"/> class.
/// </summary>
/// <param name="target">The target.</param>
public SyncGuard(object target)
{
locked = false;
if (target == null)
{
throw new ArgumentNullException("target");
}
this.target = target;
}
/**//// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="T:Ilungasoft.Helper.ObjectPooling.SyncGuard"/> is reclaimed by garbage collection.
/// </summary>
~SyncGuard()
{
RemoveLocks();
}
#endregion
Public Members#region Public Members
/**//// <summary>
/// Locks this instance.
/// </summary>
public void Lock()
{
Monitor.Enter(target);
locked = true;
}
/**//// <summary>
/// Unlocks this instance.
/// </summary>
public void Unlock()
{
Monitor.Exit(target);
locked = false;
}
/**//// <summary>
/// Removes the locks.
/// </summary>
public void RemoveLocks()
{
if (locked)
{
Unlock();
}
}
#endregion
}
}
ObjectPool - 池对象
这个类自然是我们的主角了,提供一个对象池需要的接口。这是一个泛型类,有一个类型参数<ObjectType>,这个参数指定需要返回的对象的类型,可以是一个接口。如果您指定的ObjectType可以直接通过new实例化,则可以使用默认构造函数构造Pool; 如果您希望用另一个类(ObjectType的子类或者可实例化的接口实现类),那么,您需要用第二个构造函数来实例化Pool,显式的指定Type。如果,你的类不能通过new直接实例化,或者你希望在实例化后做一些自定义的初始化,那么,后面Teddy也会向您演示怎样继承ObjectPool类获得更大灵活性的自定义。
首先,让我们来看看构造函数,共有两个,上面解释过就不多解释了:
public ObjectPool()
public ObjectPool(Type concreteType)
再来看看主要的Public方法:
public ObjectPool<ObjectType> CreatePool(bool canExhaust) //当实例化一个Pool后,必须显式的调用该函数初始化Pool。
public ObjectPool<ObjectType> CreatePool() //该函数等价于调用上面参数的版本,canExhaust取默认值true
public void DestroyPool() //当您不再需要该Pool时,清空Pool
public ObjectType Draw() //从Pool中获取一个空闲的对象实例
public void Return(ObjectType obj) //当使用完一个对象,必须显式的将对象返回Pool,否则,Pool会认为该对象一直在被使用
下面来说说Protected的Field和方法,Protected的成员用于在继承扩展该类时使得子类可访问:
protected Type type; //实际实例化的对象类型
protected int minSize = PoolableAttribute.DefaultMinPoolSize;
protected int maxSize = PoolableAttribute.DefaultMaxPoolSize;
protected int preferedSize = PoolableAttribute.DefaultPreferedPoolSize;
protected long creationTimeout = PoolableAttribute.DefaultCreationTimeout;
protected static int DefaultGrowSize = 16; //每次对象不够用时,Pool自动新增实例化的对象数
protected virtual void ObtainTypeInformation() //该函数的基类实现,将会读取您指定的对象类型的PoolableAttribute的参数,如果您不希望从PoolableAttribute获取参数而希望从其它地方读取或设定这些参数,可以override该函数
protected virtual ObjectType CreatePoolableObject() //该函数用于创建一个新的对象实例,默认实现当然是简单的new了,如果您希望通过Factory或其它方法来实例化,或者您希望附加一些实例化对象后的初始化工作,可以override该函数
protected virtual bool IsFreeObjectToMuch() //判断Pool中线的对象是否太多了,如果太多了,可能需要销毁一些,节省内存空间,您可以override它以指定自定义的判断逻辑
protected virtual void ShrinkPool() //该函数在基类内,一般会在Draw一个Object之前,先判断IsFreeObjectToMuch(),如果返回true,则调用该函数free一些空闲对象,如果,您希望是用自定义的free逻辑,可以override该函数
using
System;
using
System.Collections.Generic;
using
System.Threading;
namespace
Ilungasoft.Helper.ObjectPooling
{
public class ObjectPool<ObjectType>
{
Private Members#region Private Members
private bool canExhaust;
private SyncGuard sync;
private bool created;
private Dictionary<object, ObjectType> pool;
private Stack<ObjectType> freeObjects;
// Searches for a free item in the pool. If one is found, it is
// returned, otherwise the method returns null to notify the
// main Draw method (which would eventually grow the pool)
private ObjectType InternalDraw()
{
sync.Lock();
if (IsFreeObjectToMuch())
{
ShrinkPool();
}
// check for free objects
if (freeObjects.Count > 0) // found?
{
ObjectType po = freeObjects.Pop();
sync.Unlock();
return po;
}
sync.Unlock();
return default(ObjectType);
}
/**//// <summary>
/// grows the pool as needed : creates objectCount number of objects
/// </summary>
/// <param name="objectCount">The object count.</param>
/// <returns></returns>
private bool Grow(int objectCount)
{
sync.Lock();
// -1 means, that the pool should decide how many objects it
// can create
if (objectCount == -1) // calculate the number of objects
{
objectCount = Math.Min(maxSize - pool.Count, DefaultGrowSize);
}
if (objectCount <= 0) // pool has exhausted
{
sync.Unlock();
return false;
}
for (int i = 0; i < objectCount; i++)
{
// create brand new objects
ObjectType po = CreatePoolableObject();
// activated at InternalDraw
pool.Add(po, po); // add to pool
freeObjects.Push(po); // and to the free objects list
}
sync.Unlock();
return true;
}
#endregion
Protected Members#region Protected Members
protected Type type;
protected int minSize = PoolableAttribute.DefaultMinPoolSize;
protected int maxSize = PoolableAttribute.DefaultMaxPoolSize;
protected int preferedSize = PoolableAttribute.DefaultPreferedPoolSize;
protected long creationTimeout = PoolableAttribute.DefaultCreationTimeout;
protected static int DefaultGrowSize = 16;
/**//// <summary>
/// Obtains the type information.
/// </summary>
protected virtual void ObtainTypeInformation()
{
sync.Lock();
// get the type's custom attributes
object[] attArray = type.GetCustomAttributes(true);
Attribute[] atts = (Attribute[])attArray;
// search for our PoolableAttribute
for (int i = 0; i < atts.Length; i++)
{
if (atts[i].GetType() == typeof(PoolableAttribute))
{
// retrieve pooling information
PoolableAttribute pt = (PoolableAttribute)atts[i];
minSize = pt.MinPoolSize;
preferedSize = pt.PreferedPoolSize;
maxSize = pt.MaxPoolSize;
creationTimeout = pt.CreationTimeout;
break;
}
}
sync.Unlock();
}
/**//// <summary>
/// helper function, which creates poolable objects
/// </summary>
/// <returns></returns>
protected virtual ObjectType CreatePoolableObject()
{
return Activator.CreateInstance<ObjectType>();
}
/**//// <summary>
/// Determines whether [is free object to much].
/// </summary>
/// <returns>
/// <c>true</c> if [is free object to much]; otherwise, <c>false</c>.
/// </returns>
protected virtual bool IsFreeObjectToMuch()
{
if (freeObjects.Count > pool.Count * 3 / 4 && pool.Count / 2 > minSize)
{
return true;
}
return false;
}
/**//// <summary>
/// Shrinks the pool.
/// </summary>
protected virtual void ShrinkPool()
{
for (int i = 0; i < pool.Count / 2 && i < freeObjects.Count; i++)
{
pool.Remove(freeObjects.Pop());
}
}
#endregion
Constructors#region Constructors
public ObjectPool() : this(null)
{
}
/**//// <summary>
/// Initializes a new instance of the <see cref="T:ObjectPool<ObjectType>"/> class.
/// </summary>
/// <param name="concreteType">Type of the concrete.</param>
public ObjectPool(Type concreteType)
{
if (concreteType == null)
{
this.type = typeof(ObjectType);
}
else
{
this.type = concreteType;
}
this.canExhaust = true;
this.created = false;
this.pool = null;
this.freeObjects = null;
this.sync = new SyncGuard(this);
// obtain the PoolableAttribute and its properties
ObtainTypeInformation();
}
#endregion
Public Members#region Public Members
/**//// <summary>
/// creates the actual pool
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="canExhaust">if set to <c>true</c> [can exhaust].</param>
/// <returns></returns>
public ObjectPool<ObjectType> CreatePool(bool canExhaust)
{
if (created)
{
throw new InvalidOperationException ("Pool has been already created");
}
sync.Lock ();
this.canExhaust = canExhaust;
// create helper objects with capacity = the prefered size
pool = new Dictionary<object,ObjectType>(preferedSize);
freeObjects = new Stack<ObjectType>(preferedSize);
created = true;
sync.Unlock();
// create only "minimum pool size" objects
Grow(minSize); // create initial pool with minimum size
return this;
}
/**//// <summary>
/// Creates the pool.
/// </summary>
/// <returns></returns>
public ObjectPool<ObjectType> CreatePool()
{
return CreatePool(true);
}
/**//// <summary>
/// Destroys the pool.
/// </summary>
public void DestroyPool()
{
if (!created)
{
throw new InvalidOperationException("Pool cannot be destroyed, becuase it has not been created");
}
sync.Lock();
// clear internal helper objects
pool.Clear();
freeObjects.Clear();
created = false;
sync.Unlock();
}
/**//// <summary>
/// Draws an object from the pool. If there aren't any objects created, a new one is created, otherwise an existing one is reused.
/// </summary>
/// <returns></returns>
public ObjectType Draw()
{
if (!created)
{
throw new InvalidOperationException ("Pool has not been created");
}
// this one is a poolable type
// search for available item in the pool and return it
ObjectType po = InternalDraw();
if (po != null)
{
return po;
}
else // no free ones
{
if (Grow (-1))
{
// after the grow there will be free ones now
return InternalDraw();
}
else
{
if (canExhaust)
{
throw new InvalidOperationException("Pool exhausted!");
}
else // the pool should wait until a free object returns
{
while (po == null)
{
po = InternalDraw();
Thread.Sleep(0); // give just a scrap of time
}
return po;
} // if (canExhaust)
} // if (Grow (-1))
} // if (po != 0)
} // Draw
/**//// <summary>
/// Returns an object to the pool to be reused
/// </summary>
/// <param name="obj">The obj.</param>
public void Return(ObjectType obj)
{
if (!created)
{
throw new InvalidOperationException ("Pool has not been created");
}
if (!pool.ContainsKey(obj))
{
throw new InvalidOperationException ("Object was not created by this object pool");
}
// cleanup and return the item in the pool
sync.Lock();
freeObjects.Push(obj);
sync.Unlock();
}
#endregion
}
}
好了,类都介绍完了,下面来看看使用示例
1、使用PoolableAttribute修饰一个希望被Pooled的类
[Poolable(MinPoolSize
=
1
, PreferedPoolSize
=
16
, MaxPoolSize
=
100
)]
public
class
About : Entity
<
About
>
, IEntity
{
//
}
2、继承ObjectPool,实现特定的需求。在这个例子中,EntityPool是一个用于PoolEntity的池,我的Entity的实际类型是运行时Emit生成的,因此,只能通过Factory构造,因此,我override了CreatePooledObject方法用Factory方法代替默认实现,并且,我不希望通过PoolableAttribute来设置Pool的参数,所以,我又override了ObtainTypeInformation方法。
using
System;
using
System.Collections.Generic;
using
System.Text;
namespace
Ilungasoft.Helper.Data
{
internal class EntityPool<IEntityType> : ObjectPooling.ObjectPool<IEntityType>
where IEntityType : IEntity
{
Constructors#region Constructors
/**//// <summary>
/// Initializes a new instance of the <see cref="T:EntityPool<IEntityType>"/> class.
/// </summary>
/// <param name="concreteType">Type of the concrete.</param>
/// <param name="minPoolSize">Size of the min pool.</param>
/// <param name="preferedPoolSize">Size of the prefered pool.</param>
/// <param name="maxPoolSize">Size of the max pool.</param>
/// <param name="creationTimeout">The creation timeout.</param>
public EntityPool(Type concreteType, int minPoolSize, int preferedPoolSize, int maxPoolSize, long creationTimeout)
: this(concreteType, minPoolSize, preferedPoolSize, maxPoolSize)
{
this.creationTimeout = creationTimeout;
}
/**//// <summary>
/// Initializes a new instance of the <see cref="T:EntityPool<IEntityType>"/> class.
/// </summary>
/// <param name="concreteType">Type of the concrete.</param>
/// <param name="minPoolSize">Size of the min pool.</param>
/// <param name="preferedPoolSize">Size of the prefered pool.</param>
/// <param name="maxPoolSize">Size of the max pool.</param>
public EntityPool(Type concreteType, int minPoolSize, int preferedPoolSize, int maxPoolSize)
: base(concreteType)
{
this.minSize = minPoolSize;
this.preferedSize = preferedPoolSize;
this.maxSize = maxPoolSize;
}
/**//// <summary>
/// Initializes a new instance of the <see cref="T:EntityPool<IEntityType>"/> class.
/// </summary>
/// <param name="concreteType">Type of the concrete.</param>
public EntityPool(Type concreteType)
: base(concreteType)
{
}
#endregion
Overriden Members#region Overriden Members
/**//// <summary>
/// helper function, which creates poolable objects
/// </summary>
/// <returns></returns>
protected override IEntityType CreatePoolableObject()
{
return (IEntityType)Activator.CreateInstance(this.type);
}
/**//// <summary>
/// Obtains the type information.
/// </summary>
protected override void ObtainTypeInformation()
{
// null is ok for this class
}
#endregion
}
}
3、那么该如何使用Pool?下面演示使用上面这个继承自ObjectPool的EntityPool,如果直接使用ObjectPool完全类似。需要说明一下的是,因为我需要Pool的类型是在运行时由Factory通过Emit生成的,而返回类型About是一个接口,因此,我要在构造EntityPool是指定实际实例化Type。而PooledObjectFactory内使用EntityPool来创建和管理对象。
using
System;
namespace
Ilungasoft.Helper.TestApp.DomainObject2
{
public interface About: Ilungasoft.Helper.Data.IEntity
{
int ID { get; set; }
string Title { get; set; }
string Content { get; set; }
bool Deletable { get; set; }
int Order { get; set; }
}
}
public
class
PooledEntityFactory
<
IEntityType
>
: EntityFactory
<
IEntityType
>
where IEntityType : IEntity
{
private static EntityPool<IEntityType> pool;
/**//// <summary>
/// Initializes the <see cref="T:PooledEntityFactory<IEntityType>"/> class.
/// </summary>
static PooledEntityFactory()
{
pool = new EntityPool<IEntityType>(EntityFactory<IEntityType>.CreateObject().GetType());
pool.CreatePool();
}
/**//// <summary>
/// Creates the object.
/// </summary>
/// <returns></returns>
public new static IEntityType CreateObject()
{
return pool.Draw();
}
//
}
下面是使用PooledObjectFactory类创建和返回对象实例的示例
About obj1
=
PooledEntityFactory
<
About
>
.CreateObject();
//...
PooledEntityFactory
<
About
>
.ReturnObject(obj1);
下载ObjectPool源码
Helper.ObjectPooling.zip
//文章结束