对象池 IObjectPool -- ESBasic 可复用的.NET类库(15)

简介: 1.缘起:     对象池应该是一个“历史悠久”的概念了,像我们经常说的线程池、还有ADO.NET中的数据库连接池等,都属于对象池的应用。     我们的应用有时也会碰到需要使用对象池的情况,我举个例子说明一下。

1.缘起:

    对象池应该是一个“历史悠久”的概念了,像我们经常说的线程池、还有ADO.NET中的数据库连接池等,都属于对象池的应用。

    我们的应用有时也会碰到需要使用对象池的情况,我举个例子说明一下。假设,我们需要记录某个类MyClass的每个方法每次被调用时方法执行所消耗的时间,而且,这个类是使用在多线程的环境中的,每个方法都可以同时在多个线程中执行,不需要被同步,这样可以使并发达到最大。

    好,我们可以使用Stopwatch这个类来准确地记录每个方法的时间,关键是怎么使用它?为MyClass定义一个Stopwatch类型的成员变量,然后在每个方法的开始调用这个成员的Start以启动计时,在方法返回之前记录耗费的时间,然后Reset这个Stopwatch成员。

    这种方案只有在一种情况下可以良好工作,那就是要求对MyClass的所有方法的调用都是同步的,而且多个线程调用同一个方法时也必须被同步,否则,Stopwatch的计时就会错乱了。

    那么如何解决了?实际上很简单,我们不需要为MyClass定义一个Stopwatch类型的成员变量,而是在每个方法的入口处,new一个Stopwatch类型的局部变量,这个局部变量只服务于当前方法的一次调用。也就是说,对MyClass的任何一个方法的任何一次调用,都会产生一个Stopwatch类型的对象专门用于这次调用的计时工作,当这次调用返回后,该Stopwatch对象就可以被销毁了。这样就可以达到我们最初假设的需求。

    从解决方案我们看到,每次调用都会新建一个Stopwatch对象,当调用返回时,这个对象就没有存在的价值而可以被销毁了。这样的结果就是会反复地创建并销毁Stopwatch类型的对象。

    这正是使用对象池的一个绝佳场合,我们把一些可用的Stopwatch对象放进对象池中,每次方法被调用时,就向对象池租借一个Stopwatch对象来进行计时,调用返回时,再将这个对象归还给对象池即可。这样就避免了Stopwatch对象的重复创建和销毁。

    我设计的ESBasic.ObjectManagement.Pool.IObjectPool就是一个通用的对象池,它是泛型的,所以可以池化存储不同类型的对象。

 

2.适用场合:

    根据我们上面的描述,我们可以总结出当有类似以下的需求时,可以使用对象池技术。

(1)某个类型的对象经常被重复的创建、销毁。

(2)每个该类型的对象被使用的时间都很短

(3)使用一个共享的对象无法达到系统的要求(比如会限制最大并发量)。

(4)相对于新建或销毁一个对象来说,清除对象的状态要容易得多。

 

3.设计思想与实现

IObjectPool接口的定义如下:

    public   interface   IObjectPool < TObject >   where  TObject :  class
    {
        
///   <summary>
        
///  MinObjectCount 对象池中最少同时存在的对象数。
        
///   </summary>
         int  MinObjectCount {  get ; set ; }

        
///   <summary>
        
///  MaxObjectCount 对象池中最多同时存在的对象数。
        
///   </summary>
         int  MaxObjectCount {  get ; set ; }

        
///   <summary>
        
///  DetectSpanInMSecs 当池中没有空闲的对象且数量已达到MaxObjectCount时,如果这时发生Rent调用,则检测空闲对象的时间间隔。
        
///  默认值为10ms。 
        
///   </summary>
         int  DetectSpanInMSecs {  get ; set ; }

        
///   <summary>
        
///  PooledObjectCreator 用于创建池中对象的创建器。默认为DefaultPooledObjectCreator
        
///   </summary>
         IPooledObjectCreator < TObject >  PooledObjectCreator {  set ; }

        
void  Initialize();

        TObject Rent();
        
void  GiveBack(TObject obj);
    }

 

这个接口有一个泛型参数:TObject,表示我们要池化的对象的类型。泛型约束表明能够放入对象池中的对象必须是引用类型。

MinObjectCount属性指示对象池在初始化的时候就必须确保池中存在的对象的数量。

MaxObjectCount属性表示对象池最多能够容纳的对象数量。

如果一个对象被租借出去,则对象池会将其状态标记为“繁忙”的;如果一个对象没有被租借出去或被归还,则其状况就是“空闲”的。

对于ObjectPool的实现,要注意以下几点:

(1)对象池是多线程安全的,可以在多线程的环境下使用。我们对内部的集合进行了加锁控制。

(2)对象池并不直接负责对象的创建工作,它把这项职责委托给了池化对象创建者IPooledObjectCreator

(3)池化对象创建者IPooledObjectCreator不仅负责对象的创建工作,而且也负责清除对象的状态(Reset方法)。在GiveBack方法的内部就有调用Reset方法来清除对象的遗留状态的。IPooledObjectCreator接口的定义如下所示:

    /// <summary>
    
/// IPooledObjectCreator 池化对象创建者。用于创建被池缓存的对象。并能清除对象的状态。

    /// </summary>
    public interface IPooledObjectCreator<TObject> where TObject : class
    {
        TObject Create();
        
void Reset(TObject obj);
    }

 

4. 使用时的注意事项

(1)当外部调用Rent方法向对象池租借一个对象时,如果对象池中没有“空闲”的对象,并且池中的对象的数量已经达到了MaxObjectCount,那么这时该如何处理了?ObjectPool采用的策略是选择等待,等待直到有对象变成“空闲”,否则就一直阻塞当前线程。你必须注意到ObjectPool采用的这个策略可能会与你的期望不一致。

(2)当对象池中的空闲对象很多时,即使已经远远地大于了MinObjectCount的值,对象池也不会释放其中的某些对象,而是一直保持着。MinObjectCount只是决定了池在初始化的时候应该创建的对象的数量以备用。

(3)基于上面的两点原因,所以我们在具体应用时需要谨慎地为MaxObjectCount设定一个合理的值。如果这个值太小,可能会使得阻塞线程的情况经常发生。当然,这个值也不是设得越大越好,因为如果平时空闲的对象很多,就表示要占用更多的资源而却没有发挥出相应的价值。

(4)一个从池中借出的对象在被归还回给池的时候,必须把上次使用时遗留的状态清除掉,否则后面的租借者可能会误用其遗留的状态。

(5)如果清除一个对象的状态很不容易,相反创建和销毁一个对象却非常容易,那么这时可以考虑不使用对象池,而是每次new一个对象来使用,使用完了就丢弃掉。

 

5.扩展

       如果我们要池化的对象是没有状态的,而且其类型也有不带参数的默认构造函数,那么我们可以直接使用ESBasic提供的默认池化对象创建者DefaultPooledObjectCreatorDefaultPooledObjectCreator使用反射创建对象,并且Reset方法也不对对象做任何供动作――因为对象本身就是没有状态的,所以也就不存在清除其状态的需要了。

 

注: ESBasic已经开源,点击这里下载源码。
   
ESBasic开源前言

 

目录
相关文章
|
IDE API 开发工具
拦截|篡改|伪造.NET类库中不限于public的类和方法
本文除了回顾拦截.NET类库中的方法,实现方法参数的篡改、方法返回结果的伪造,再着重介绍.NET类库中非public类及方法如何拦截。
拦截|篡改|伪造.NET类库中不限于public的类和方法
|
3月前
|
开发框架 .NET Linux
2款高效的.NET二维码生成类库
2款高效的.NET二维码生成类库
|
3月前
|
XML 开发框架 数据格式
.Net Core 开发框架,支持多版本的类库
.Net Core 开发框架,支持多版本的类库
56 0
|
4月前
|
人工智能 开发框架 Devops
.NET技术概览:** 本文探讨了.NET的核心特性,包括多语言支持、Common Language Runtime、丰富的类库和跨平台能力,强调其在企业级、Web、移动及游戏开发中的应用。
【7月更文挑战第4天】.NET技术概览:** 本文探讨了.NET的核心特性,包括多语言支持、Common Language Runtime、丰富的类库和跨平台能力,强调其在企业级、Web、移动及游戏开发中的应用。此外,讨论了.NET如何通过性能优化、DevOps集成、AI与ML支持以及开源策略应对未来挑战,为开发者提供强大工具,共创软件开发新篇章。
51 3
|
4月前
|
人工智能 前端开发 Devops
NET技术在现代开发中的影响力日益增强,本文聚焦其核心价值,如多语言支持、强大的Visual Studio工具、丰富的类库和跨平台能力。
【7月更文挑战第4天】**.NET技术在现代开发中的影响力日益增强,本文聚焦其核心价值,如多语言支持、强大的Visual Studio工具、丰富的类库和跨平台能力。实际应用涵盖企业系统、Web、移动和游戏开发,以及云服务。面对性能挑战、容器化、AI集成及跨平台竞争,.NET持续创新,开发者应关注技术趋势,提升技能,并参与社区,共同推进技术发展。**
37 1
|
4月前
|
开发框架 .NET API
.NET Core 和 .NET 标准类库项目类型有什么区别?
在 Visual Studio 中,可创建三种类库:.NET Framework、.NET Standard 和 .NET Core。.NET Standard 是规范,确保跨.NET实现的API一致性,适用于代码共享。.NET Framework 用于特定技术,如旧版支持。.NET Core 库允许访问更多API但限制兼容性。选择取决于兼容性和所需API:需要广泛兼容性时用.NET Standard,需要更多API时用.NET Core。.NET Standard 替代了 PCL,促进多平台共享代码。
|
6月前
|
C# 数据安全/隐私保护
一款实用的.NET Core加密解密工具类库
一款实用的.NET Core加密解密工具类库
|
11月前
|
存储 文字识别 C#
一个基于百度飞桨封装的.NET版本OCR工具类库 - PaddleOCRSharp
一个基于百度飞桨封装的.NET版本OCR工具类库 - PaddleOCRSharp
400 0
|
存储 安全 API
10分钟学会Visual Studio将自己创建的类库打包到NuGet进行引用(net,net core,C#)
10分钟学会Visual Studio将自己创建的类库打包到NuGet进行引用(net,net core,C#)
|
XML .NET C#
.NET Framework 类库——C#命名空间大全
引用地址:https://msdn.microsoft.com/zh-cn/library/gg145045.aspx C# using引用时,不知道有哪些命名空间,这下转载收集一篇,方面查找使用。
1178 0