C#2.0 - Object Pool 简单实现
本文介绍本人的一个简单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中获取空闲对象的请求会要求等待一个对象空闲出来。因此,这个参数实际上主要用于后面这种超时情形。
SyncGuard - 用于保证线程安全的锁对象
这个类比较简单,主要就是一个锁,大家看看代码应该就能明白。
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该函数
好了,类都介绍完了,下面来看看使用示例
1、使用PoolableAttribute修饰一个希望被Pooled的类
2、继承ObjectPool,实现特定的需求。在这个例子中,EntityPool是一个用于PoolEntity的池,我的Entity的实际类型是运行时Emit生成的,因此,只能通过Factory构造,因此,我override了CreatePooledObject方法用Factory方法代替默认实现,并且,我不希望通过PoolableAttribute来设置Pool的参数,所以,我又override了ObtainTypeInformation方法。
3、那么该如何使用Pool?下面演示使用上面这个继承自ObjectPool的EntityPool,如果直接使用ObjectPool完全类似。需要说明一下的是,因为我需要Pool的类型是在运行时由Factory通过Emit生成的,而返回类型About是一个接口,因此,我要在构造EntityPool是指定实际实例化Type。而PooledObjectFactory内使用EntityPool来创建和管理对象。
下面是使用PooledObjectFactory类创建和返回对象实例的示例
下载ObjectPool源码
Helper.ObjectPooling.zip
//文章结束
主要的类
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
Const Members
Public Members
Properties
}
}
using System.Collections.Generic;
using System.Text;
namespace Ilungasoft.Helper.ObjectPooling
{
[AttributeUsageAttribute(AttributeTargets.Class, AllowMultiple = false)]
public class PoolableAttribute : Attribute
{
Private Members
Const Members
Public Members
Properties
}
}
SyncGuard - 用于保证线程安全的锁对象
这个类比较简单,主要就是一个锁,大家看看代码应该就能明白。
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace Ilungasoft.Helper.ObjectPooling
{
internal class SyncGuard
{
Private Members
Constructors
Public Members
}
}
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace Ilungasoft.Helper.ObjectPooling
{
internal class SyncGuard
{
Private Members
Constructors
Public Members
}
}
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
Protected Members
Constructors
Public Members
}
}
using System.Collections.Generic;
using System.Threading;
namespace Ilungasoft.Helper.ObjectPooling
{
public class ObjectPool<ObjectType>
{
Private Members
Protected Members
Constructors
Public Members
}
}
好了,类都介绍完了,下面来看看使用示例
1、使用PoolableAttribute修饰一个希望被Pooled的类
[Poolable(MinPoolSize = 1, PreferedPoolSize = 16, MaxPoolSize = 100)]
public class About : Entity<About>, IEntity
{
//
}
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
Overriden Members
}
}
using System.Collections.Generic;
using System.Text;
namespace Ilungasoft.Helper.Data
{
internal class EntityPool<IEntityType> : ObjectPooling.ObjectPool<IEntityType>
where IEntityType : IEntity
{
Constructors
Overriden Members
}
}
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; }
}
}
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();
}
//
}
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);
//...
PooledEntityFactory<About>.ReturnObject(obj1);
下载ObjectPool源码
Helper.ObjectPooling.zip
//文章结束