通用对象池ObjectPool的一种简易设计和实现方案
对象池,最简单直接的作用当然是通过池来减少创建和销毁对象次数,实现对象的缓存和复用。我们熟知的线程池、数据库连接池、TCP连接池等等都是非常典型的对象池。
一个基本的简易对象池的主要功能实现我认为应该至少包括以下三点:
1、对象的分配、销毁等管理策略
2、线程安全
3、性能
按照主要的常用功能,我们大致可以抽象出以下泛型接口IObjectPool<T>:


- IObjectPoolusing System;
- /// <summary>
- /// Simple ObjectPool Interface
- /// </summary>
- /// <typeparam name="T"></typeparam>
- public interface IObjectPool<T> : IDisposable
- {
- #region Properties
- /// <summary>
- /// Minimum enabled object count
- /// </summary>
- int MinObjCount { get; }
- /// <summary>
- /// Maximum enabled object count
- /// </summary>
- int MaxObjCount { get; }
- /// <summary>
- /// Current enabled object count
- /// </summary>
- int CurrentObjCount { get; }
- #endregion
- #region Methods
- /// <summary>
- /// Gets an item from the pool.
- /// </summary>
- /// <returns>The removed or created item.</returns>
- /// <remarks>If the pool is empty, a new item will be created and returned.</remarks>
- T GetObject();
- /// <summary>
- /// Adds the provided item into the pool.
- /// </summary>
- /// <param name="item">The item to be added.</param>
- void PutObject(T item);
- #endregion
- }
具体实现这个接口的时候,考虑到线程安全,同时为了降低线程安全控制的种种风险,我们使用framework4.0中提供的线程安全容器,示例代码中选择非常适合这种场景的线程安全队列ConcurrentQueue(时间和空间复杂度都非常合适):


- ObjectPoolusing System;
- using System.Collections.Concurrent;
- public class ObjectPool<T> : IObjectPool<T> where T : new()
- {
- #region 字段
- private readonly IProducerConsumerCollection<T> collection;
- private int _minObjCount = 0;
- private int _maxObjCount = 0;
- #endregion
- #region 构造函数
- public ObjectPool()
- : this(1, 8)
- {
- }
- public ObjectPool(int minObjCount, int maxObjCount)
- {
- if (minObjCount < 1)
- {
- throw new ArgumentException("minObjCount cannot be less than zero");
- }
- if (maxObjCount < 1)
- {
- throw new ArgumentException("maxObjCount cannot be less than zero");
- }
- if (maxObjCount < minObjCount)
- {
- throw new ArgumentException("maxObjCount cannot be less than minObjCount");
- }
- this._minObjCount = minObjCount;
- this._maxObjCount = maxObjCount;
- collection = new ConcurrentQueue<T>();
- var objCount = (minObjCount + maxObjCount) / 2;
- for (int i = 0; i < objCount; i++) //初始化的对象个数取折中方案
- {
- var item = new T();
- collection.TryAdd(item);
- }
- }
- #endregion
- #region 属性
- public int MinObjCount { get { return _minObjCount; } }
- public int MaxObjCount { get { return _maxObjCount; } }
- public int CurrentObjCount
- {
- get
- {
- if (collection != null)
- {
- return collection.Count;
- }
- return 0;
- }
- }
- #endregion
- #region 方法
- public T GetObject()
- {
- T item = default(T);
- var isOK = collection.TryTake(out item);
- //如取出对象为空 此处采用悲观对象分配方案,直接创建一个新对象,保证有对象可用
- if (item == null || isOK == false)
- {
- item = new T();
- this.PutObject(item); //新创建的对象放入容器中
- Console.WriteLine("新创建的对象放入容器中");
- }
- return item;
- }
- public void PutObject(T item)
- {
- if (item == null)
- {
- return;
- }
- collection.TryAdd(item);
- //TryTrimToMax(); //清除大于队列最大可用的项
- }
- public void Dispose()
- {
- }
- #endregion
- #region 辅助方法
- /// <summary>
- /// 清除大于队列最大可用的项
- /// </summary>
- private void TryTrimToMax()
- {
- while (this.collection.Count > this.MaxObjCount)
- {
- var item = default(T);
- this.collection.TryTake(out item);
- Console.WriteLine("清除了大于队列最大可用的项");
- }
- }
- #endregion
- }
其中,GetObject方法即对象的获取至少有两种策略:
1、悲观对象创建策略
这种策略在获取对象时,“悲观”地认为当对象池中暂无可用对象时,等待是会发生很久时间的,必须保证能够返回一个可用对象,所以不如去掉等待直接创建一个对象返回。虽然并发较高的场景下这有可能造成系统中对象总数暂时超出了最大对象数量限制,但好处是我们可以保证系统有足够的对象可用,不会因为没可用对象或等待创建可用对象而使上层逻辑受阻。
对于对象超过最大对象个数限制的情况,我们完全可以对外再暴露一个接口方法(示例为TryTrimToMax),当系统并发压力减轻时,可调用该方法清理多余的对象。
上面ObjectPool示例代码中我们采取的就是悲观创建对象的方案。
2、乐观对象创建策略
这种策略“乐观”地认为,当对象池中暂无可用对象时,所有对象的消费者会在用完对象后及时的返回对象池中。
如果对象池中无可用对象,那么当前请求者可能会选择Sleep或者Wait或WaitOne等方式等待。如果更乐观的话,还可以采用类似lock-free的方式多尝试去获取对象(不完全是CAS的那种lock-free),直到获取对象为止。
这种方式的优点显而易见,它的空间使用率高,不会造成对象超过最大上限。但它的缺点也很明显,在一个高并发的可用对象已经供不应求的环境下,这种策略多数实现都不是wait-free的,没有获取到对象的请求调用只能排队等待,这就极大地降低了系统吞吐量,原来的对象池是为了提高性能的,现在则成了系统性能瓶颈的重要原因。
上述示例代码中,泛型参数T有where限制,可通过Func委托间接去掉这个限制,当然构造函数会略微变得复杂:


- ObjectPoolusing System;
- using System.Collections.Concurrent;
- public class ObjectPool<T> : IObjectPool<T>
- {
- #region 字段
- private readonly IProducerConsumerCollection<T> collection;
- private readonly Func<T> _generator;
- private int _minObjCount = 0;
- private int _maxObjCount = 0;
- #endregion
- #region 构造函数
- public ObjectPool(Func<T> generator)
- : this(generator, 1, 8)
- {
- }
- public ObjectPool(Func<T> generator, int minObjCount, int maxObjCount)
- {
- if (generator == null)
- {
- throw new ArgumentNullException("generator cannot be null");
- }
- if (minObjCount < 1)
- {
- throw new ArgumentException("minObjCount cannot be less than zero");
- }
- if (maxObjCount < 1)
- {
- throw new ArgumentException("maxObjCount cannot be less than zero");
- }
- if (maxObjCount < minObjCount)
- {
- throw new ArgumentException("maxObjCount cannot be less than minObjCount");
- }
- this._generator = generator;
- this._minObjCount = minObjCount;
- this._maxObjCount = maxObjCount;
- collection = new ConcurrentQueue<T>();
- var objCount = (minObjCount + maxObjCount) / 2;
- for (int i = 0; i < objCount; i++) //初始化的对象个数取折中方案
- {
- var item = _generator();
- collection.TryAdd(item);
- }
- }
- #endregion
- #region 属性
- public int MinObjCount { get { return _minObjCount; } }
- public int MaxObjCount { get { return _maxObjCount; } }
- public int CurrentObjCount
- {
- get
- {
- if (collection != null)
- {
- return collection.Count;
- }
- return 0;
- }
- }
- #endregion
- #region 方法
- public T GetObject()
- {
- T item = default(T);
- var isOK = collection.TryTake(out item);
- //如取出对象为空 此处采用悲观对象分配方案,直接创建一个新对象,保证有对象可用
- if (item == null || isOK == false)
- {
- item = _generator();
- this.PutObject(item); //新创建的对象放入容器中
- Console.WriteLine("新创建的对象放入容器中");
- }
- return item;
- }
- public void PutObject(T item)
- {
- if (item == null)
- {
- return;
- }
- collection.TryAdd(item);
- //TryTrimToMax(); //清除大于队列最大可用的项
- }
- public void Dispose()
- {
- }
- #endregion
- #region 辅助方法
- /// <summary>
- /// 清除大于队列最大可用的项
- /// </summary>
- private void TryTrimToMax()
- {
- while (this.collection.Count > this.MaxObjCount)
- {
- var item = default(T);
- this.collection.TryTake(out item);
- Console.WriteLine("清除了大于队列最大可用的项");
- }
- }
- #endregion
- }
可通过下列代码进行测试验证:


- ObjectPoolTestusing System;
- using System.Threading;
- using System.Threading.Tasks;
- class UserService
- {
- public void CheckUser(int i)
- {
- //Console.WriteLine("check user:{0}", i);
- Thread.Sleep(1);
- }
- }
- public class Program
- {
- static void Main(string[] args)
- {
- TestObjectPool();
- for (int a = 0; a < 10; a++)
- {
- Console.WriteLine("测试:{0}", a + 1);
- TestObjectPool();
- }
- Console.Read();
- }
- static void TestObjectPool()
- {
- var recordCount = 10000;
- using (var pool = new ObjectPool<UserService>(() => { return new UserService(); }, 1, 16))
- //using (var pool = new ObjectPool<UserService>(() => { return new UserService(); }, 1, 128))
- {
- Console.WriteLine("Init:current obj count:{0}", pool.CurrentObjCount);
- Parallel.For(0, recordCount, i =>
- {
- var service = pool.GetObject();
- service.CheckUser(i);
- Thread.Sleep(2);
- pool.PutObject(service);
- });
- Console.WriteLine("current obj count:{0}", pool.CurrentObjCount);
- Console.WriteLine("min obj count:{0}", pool.MinObjCount);
- Console.WriteLine("max obj count:{0}", pool.MaxObjCount);
- Console.WriteLine("====================");
- }
- }
- }
本文的对象池实现较为简洁,只有基本的存取功能,其他功能如对象的销毁等可以继续扩展。还要感慨下幸好有了线程安全容器,果然可以成倍地提高生产效率,而且代码更加简洁优雅。
参考:
http://blogs.msdn.com/b/pfxteam/archive/2010/04/13/9990427.aspx
http://code.msdn.microsoft.com/parextsamples
通用对象池ObjectPool的一种简易设计和实现方案的更多相关文章
- Egret中的对象池ObjectPool
为了可以让对象复用,防止大量重复创建对象,导致资源浪费,使用对象池来管理. 对象池具体含义作用,自行百度. 一 对象池A 二 对象池B 三 字符串key和对象key的效率 一 对象池A /** * 对 ...
- Java小对象的解决之道——对象池(Object Pool)的设计与应用
一.概述 面向对象编程是软件开发中的一项利器,现已经成为大多数编程人员的编程思路.很多高级计算机语言也对这种编程模式提供了很好的支持,例如C++.Object Pascal.Java等.曾经有大量的软 ...
- JS模式--通用对象池的实现
var objectPoolFactory = function (createObjFn) { var objectPool = []; return { create: function () { ...
- Java对象池技术的原理及其实现
看到一片有关于java 对象基础知识,故转载一下,同时学习一下. 摘 要 本文在分析对象池技术基本原理的基础上,给出了对象池技术的两种实现方式.还指出了使用对象池技术时所应注意的问题. 关键词 对象池 ...
- java对象池commons-pool-1.6详解(一)
自己的项目中用到了 对象池 commons-pool: package com.sankuai.qcs.regulation.protocol.client; import com.dianping. ...
- 缓冲&缓存&对象池概念的理解
一).缓冲 作用:缓解程序上下层之间的性能差异. 1).当上层组件的性能优于下层组件时加入缓冲机制可以减少上层组件对下 层组件的等待时间. 2).上层组件不需要等待下层组件接收全部数据,即可返回操作, ...
- 利用commons-pool2自定义对象池
一.为什么使用对象池 恰当地使用对象池化技术,可以有效地减少对象生成和初始化时的消耗,提高系统的运行效率.commons-pool2是Apache下一个开源的公共资源池.我们可以根据它来快速的建立 ...
- Java中对象池的本质是什么?(实战分析版)
简介 对象池顾名思义就是存放对象的池,与我们常听到的线程池.数据库连接池.http连接池等一样,都是典型的池化设计思想. 对象池的优点就是可以集中管理池中对象,减少频繁创建和销毁长期使用的对象,从而提 ...
- javascript 对象池
* 一个对象池的简单应用 tool tip tootip.html <html> <head> <meta charset="UTF-8"> & ...
随机推荐
- SER SERVER存储过程
Transact-SQL中的存储过程,非常类似于C#语言中的方法,可以重复调用.当存储过程执行一次后,可以将语句存储到缓存中,这样下次执行的时候直接使用缓存中的语句.这样就可以提高存储过程的性能. 一 ...
- static 使用,静态变量
由static修饰,属于整个类,被类对象共享, 可以由类名,对象名访问 static可以修饰变量,方法,代码块 public class HelloWorld { static String clas ...
- Ubuntu中vi常用命令
在Ubuntu中经常需要修改某些文件,这里对vi中的一些常用操作作一下总结. 1.进入vi命令 vi filename: 打开或新建文件,并将光标置于第一行首 进入文件后,处于命令行模式(comman ...
- 用Python玩转词云
第一步:引入相关的库包: #coding:utf-8 __author__ = 'Administrator' import jieba #分词包 import numpy #numpy计算包 imp ...
- js灵活打印web页面区域内容的通用方法
我们做网站,经常需要打印页面指定区域的内容,而网上关于这块的说法很多,各种各样的打印控件也不少.但许多打印方案都不怎么好,至少我不喜欢,要么封装复杂,要么难以维护.正好现在的项目也需要用到 ...
- 模板(Template)
最近阅读google chromium base container stack_container代码,深刻感觉到基础知识不扎实. // Casts the buffer in its right ...
- Browser默认书签加载过程
Browser配置默认书签——string.xml中<string-array name="bookmarks" translatable="false" ...
- Emmet基本使用方法
Emmet基本使用方法 分类: 其他文章2013-10-29 14:53 4048人阅读 评论(0) 收藏 举报 转载来自:http://www.iteye.com/news/27580 Emme ...
- mysql数据库查询pdo的用法
最早的php对mysql数据库查询是mysql和mysqli方法,后来php的新版本进一步封住了该方法,于是又pdo,抛开php框架,使用pdo查询数据,使用也是相当简便 <?php ini_s ...
- 安装完win8后,显卡一些其他的驱动没有被正常安装,此处出现问题的机器是索尼vpccw18fc
问题描述:安装完win8后机器无法正常开启,通过索尼欢迎页面按f10,修复进入,进入后一切正常,分辨率怪异,通过鲁大师检测显卡驱动以及一些细微的驱动没安装,但是鲁大师提示不支持这款显卡驱动,官网也没找 ...