热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

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接口的定义如下:

publicinterfaceIObjectPool<TObject>whereTObject:class
{
///
///MinObjectCount对象池中最少同时存在的对象数。
///

intMinObjectCount{get;set;}

///
///MaxObjectCount对象池中最多同时存在的对象数。
///

intMaxObjectCount{get;set;}

///
///DetectSpanInMSecs当池中没有空闲的对象且数量已达到MaxObjectCount时&#xff0c;如果这时发生Rent调用&#xff0c;则检测空闲对象的时间间隔。
///默认值为10ms。
///

intDetectSpanInMSecs{get;set;}

///
///PooledObjectCreator用于创建池中对象的创建器。默认为DefaultPooledObjectCreator
///

IPooledObjectCreator<TObject>PooledObjectCreator{set;}

voidInitialize();

TObjectRent();
voidGiveBack(TObjectobj);
}

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

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

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

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

对于ObjectPool的实现&#xff0c;要注意以下几点&#xff1a;

&#xff08;1&#xff09;对象池是多线程安全的&#xff0c;可以在多线程的环境下使用。我们对内部的集合进行了加锁控制。

&#xff08;2&#xff09;对象池并不直接负责对象的创建工作&#xff0c;它把这项职责委托给了池化对象创建者IPooledObjectCreator

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

///


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

///


publicinterfaceIPooledObjectCreator<TObject>whereTObject:class
{
TObjectCreate();
voidReset(TObjectobj);
}

4. 使用时的注意事项

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

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

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

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

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

5.扩展

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

注&#xff1a;ESBasic源码可到http://esbasic.codeplex.com/下载。
ESBasic讨论&#xff1a;37677395
ESBasic开源前言



推荐阅读
  • 最近遇到了一道关于哈夫曼树的编程题目,需要在下午之前完成。题目要求设计一个哈夫曼编码和解码系统,能够反复显示和处理多个项目,直到用户选择退出。希望各位大神能够提供帮助。 ... [详细]
  • vue引入echarts地图的四种方式
    一、vue中引入echart1、安装echarts:npminstallecharts--save2、在main.js文件中引入echarts实例:  Vue.prototype.$echartsecharts3、在需要用到echart图形的vue文件中引入:   importechartsfrom&amp;quot;echarts&amp;quot;;4、如果用到map(地图),还 ... [详细]
  • WCF类型共享的最佳实践
    在使用WCF服务时,经常会遇到同一个实体类型在不同服务中被生成为不同版本的问题。本文将介绍几种有效的类型共享方法,以解决这一常见问题。 ... [详细]
  • 本文将深入探讨 iOS 中的 Grand Central Dispatch (GCD),并介绍如何利用 GCD 进行高效多线程编程。如果你对线程的基本概念还不熟悉,建议先阅读相关基础资料。 ... [详细]
  • 包含phppdoerrorcode的词条 ... [详细]
  • 本文详细介绍了Java反射机制的基本概念、获取Class对象的方法、反射的主要功能及其在实际开发中的应用。通过具体示例,帮助读者更好地理解和使用Java反射。 ... [详细]
  • PBO(PixelBufferObject),将像素数据存储在显存中。优点:1、快速的像素数据传递,它采用了一种叫DMA(DirectM ... [详细]
  • iOS snow animation
    CTSnowAnimationView.hCTMyCtripCreatedbyalexon1614.Copyright©2016年ctrip.Allrightsreserved.# ... [详细]
  • Leetcode学习成长记:天池leetcode基础训练营Task01数组
    前言这是本人第一次参加由Datawhale举办的组队学习活动,这个活动每月一次,之前也一直关注,但未亲身参与过,这次看到活动 ... [详细]
  • 兆芯X86 CPU架构的演进与现状(国产CPU系列)
    本文详细介绍了兆芯X86 CPU架构的发展历程,从公司成立背景到关键技术授权,再到具体芯片架构的演进,全面解析了兆芯在国产CPU领域的贡献与挑战。 ... [详细]
  • 2020年9月15日,Oracle正式发布了最新的JDK 15版本。本次更新带来了许多新特性,包括隐藏类、EdDSA签名算法、模式匹配、记录类、封闭类和文本块等。 ... [详细]
  • 本文介绍了Spring 2.0引入的TaskExecutor接口及其多种实现,包括同步和异步执行任务的方式。文章详细解释了如何在Spring应用中配置和使用这些线程池实现,以提高应用的性能和可管理性。 ... [详细]
  • HTTP(HyperTextTransferProtocol)是超文本传输协议的缩写,它用于传送www方式的数据。HTTP协议采用了请求响应模型。客服端向服务器发送一 ... [详细]
  • 本文介绍了如何在 Spring 3.0.5 中使用 JdbcTemplate 插入数据并获取 MySQL 表中的自增主键。 ... [详细]
  • 浅析python实现布隆过滤器及Redis中的缓存穿透原理_python
    本文带你了解了位图的实现,布隆过滤器的原理及Python中的使用,以及布隆过滤器如何应对Redis中的缓存穿透,相信你对布隆过滤 ... [详细]
author-avatar
chucai
这个家伙很懒,什么也没留下,只留下了这个默认个签!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有