设计一个对象池(Anno.XObjectPool)
设计一个.net对象池
对象池对于创建开销比较大的对象来说很有意义,为了优化程序的运行速度、避免频繁创建销毁开销比较大的对象,我们可以通过对象池来复用创建开销大的对象。对象池的思路比较简单,事先创建好一批对象,放到一个集合中,以后每当程序需要新的对象时候,都从对象池里获取,每当程序用完该对象后,都把该对象归还给对象池。这样会避免重复的对象创建,提高程序性能。
应用场景
在Anno微服务框架中的使用,由于客户端调用微服的时候需要建立Socket连接,频繁的创建和销毁连接会有很大的开销。所以我们设想我们如果可以重复利用这些对象那么性能上也会是很大的提升。这时候我们就需要一个对象池来存放这些可以重复利用的对象。不仅如此我们还需要可以控制对象池的最大存活对象数量、最小闲置对象数量、最大闲置对象数量、如何创建对象、销毁对象、定期清理闲置对象。(网上没找到好用的于是开始造我们的轮子)
Install-Package Anno.XObjectPool -Version 1.0.3.4
Xpool核心代码

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace Anno.XObjectPool
{
public class XPool<T> : IDisposable
{
private bool disposed;
private XPoolConfiguration xcfg;
/// <summary>
/// 初始化对象池
/// </summary>
/// <param name="createObject">创建XObject对象</param>
/// <param name="activeXObject"> 获取XObject对象之前验证True 有效</param>
public XPool(Func<T> createObject, Func<T, bool> activeXObject = null) : this(new XPoolConfiguration()
{
MaxActive = 1000,
MaxIdle = 400,
MinIdle = 10
}, createObject, activeXObject)
{ }
/// <summary>
/// 初始化对象池
/// </summary>
/// <param name="maxActive">最大活动数量</param>
/// <param name="minIdle">最小空闲数量</param>
/// <param name="maxIdle">最大空闲数量</param>
/// <param name="createObject">创建XObject对象</param>
/// <param name="activeXObject"> 获取XObject对象之前验证True 有效</param>
public XPool(int maxActive, int minIdle, int maxIdle, Func<T> createObject, Func<T, bool> activeXObject = null)
{
xcfg = new XPoolConfiguration()
{
MaxActive = maxActive,
MaxIdle = maxIdle,
MinIdle = minIdle
};
pools = new ConcurrentStack<XObject<T>>();
ResetEvent = new AutoResetEvent(false);
if (createObject != null)
{
CreateXObject = createObject;
}
else
{
throw new ArgumentNullException("createObject 不能为空");
}
if (activeXObject != null)
{
ActiveXObject = activeXObject;
}
Parallel.For(0, minIdle, x =>
{
pools.Push(new XObject<T>()
{
Value = CreateXObject.Invoke(),
LastDateTime = DateTime.Now,
Pool = this
});
});
StartTaskClearLongIdleXObject();
}
/// <summary>
/// 初始化对象池
/// </summary>
/// <param name="xcfg">对象池配置</param>
/// <param name="createObject">创建XObject对象</param>
/// <param name="activeXObject"> 获取XObject对象之前验证True 有效</param>
public XPool(XPoolConfiguration xcfg, Func<T> createObject, Func<T, bool> activeXObject = null) : this(xcfg.MaxActive, xcfg.MinIdle, xcfg.MaxIdle, createObject, activeXObject)
{ }
private ConcurrentStack<XObject<T>> pools;
private int _activedTransportCount = 0;
private AutoResetEvent ResetEvent { get; set; }
/// <summary>
/// 活动链接数量
/// </summary>
public int ActivedTransportCount => _activedTransportCount; /// <summary>
/// 原子性增加 活动链接数量
/// </summary>
private void InterlockedIncrement()
{
Interlocked.Increment(ref _activedTransportCount);
}
/// <summary>
/// 原子性减少 活动链接数量
/// </summary>
private void InterlockedDecrement()
{
Interlocked.Decrement(ref _activedTransportCount);
} public XObject<T> Borrow(TimeSpan? timeout = null)
{
if (!pools.TryPop(out XObject<T> xobj))
{
if (pools.Count < xcfg.MinIdle && _activedTransportCount < xcfg.MaxActive)
{
pools.Push(new XObject<T>()
{
Value = CreateXObject.Invoke(),
LastDateTime = DateTime.Now,
Pool = this
});
}
if (!pools.Any() && _activedTransportCount >= xcfg.MaxActive)
{
int millisecondsTimeout = 20000;
if (timeout.HasValue && timeout.Value.TotalMilliseconds > 0)
{
millisecondsTimeout = (int)timeout.Value.TotalMilliseconds;
}
bool result = ResetEvent.WaitOne(millisecondsTimeout);
if (!result)
{
throw new TimeoutException($"Timeout对象池等待超时!");
}
}
if (!pools.TryPop(out xobj))
{
xobj = new XObject<T>()
{
Value = CreateXObject.Invoke(),
LastDateTime = DateTime.Now,
Pool = this
};
}
}
InterlockedIncrement();
//借出之前判断对象是否有效
if (!ActiveXObject(xobj.Value))
{
throw new InvalidOperationException("对象无效,请在有效性检测函数activeXObject中设置有效性");
}
return xobj;
}
public void Return(XObject<T> xObject, bool isDispose = false)
{
if (xObject == null)
{
throw new ArgumentNullException("xObject 不能为空!");
}
/*
* 主动释放的释放
* 超出最大闲置数量的释放
* 无效的释放
*/
if (isDispose || _activedTransportCount > xcfg.MaxIdle || !ActiveXObject(xObject.Value))
{
DisposeXObject(xObject);
xObject.Pool = null;
InterlockedDecrement();
return;
}
xObject.LastDateTime = DateTime.Now;
pools.Push(xObject);
InterlockedDecrement();
ResetEvent.Set();
} private void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
try
{
while (pools.TryPop(out XObject<T> xobj))
{
//Pool 释放的时候XObject不再归还到Pool
DisposeXObject(xobj);
xobj.Pool = null;
}
}
catch (Exception)
{ }
}
disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
} /// <summary>
/// 创建XObject对象
/// </summary>
public Func<T> CreateXObject { get; set; } = () => { return default(T); };
/// <summary>
/// 获取XObject对象之前验证True 有效
/// </summary>
public Func<T, bool> ActiveXObject { get; set; } = x => { return true; };
/// <summary>
/// 释放XObject时候触发
/// </summary>
public Action<XObject<T>> DisposeXObject { get; set; } = x => { };
/// <summary>
/// 移除长度为count的元素
/// </summary>
/// <param name="count">除元素的长度count</param>
private void DisposeLongIdleXObject(int count)
{
int startIndex = pools.Count - count;
XObject<T>[] popXObjects = new XObject<T>[count];
pools.TryPopRange(popXObjects, 0, count);
for (int i = 0; i < popXObjects.Length; i++)
{
Return(popXObjects[i], true);
}
}
/// <summary>
/// 每隔10秒检测一次清理30秒未使用的对象数量的对象
/// (如果存在对象30未使用,说明对象池有对象长时间闲置未使用)则从头部弹出一定数量的对象释放掉
/// </summary>
private void StartTaskClearLongIdleXObject()
{
Task.Factory.StartNew(async () =>
{
while (!disposed)
{
await Task.Delay(10000);
try
{
var removeCount = 0;
var now = DateTime.Now.AddSeconds(-30);
var _pools = pools.ToList();
for (int i = _pools.Count - 1; i >= xcfg.MinIdle; i--)
{
if (_pools[i].LastDateTime < now)
{
removeCount++;
}
}
if (removeCount > 0 && removeCount <= (pools.Count - xcfg.MinIdle))
{
DisposeLongIdleXObject(removeCount);
}
}
finally { }
}
}, TaskCreationOptions.LongRunning);
}
}
}
初始化一个对象池
最大活动对象数量 50个,最小闲置对象数量2个,最大闲置数量20个。
var UserPool = new XPool<User>(50, 2, 20, () =>
{
int age = Interlocked.Increment(ref _activedTransportCount);
return new User()
{
Age = age,
Name = $"Name{age}" };
});
并行调用
200个并行调用
Parallel.For(0, 200, x =>
{
using (var user = UserPool.Borrow())
{
Console.WriteLine($"Age:{user.Value.Age},Name:{user.Value.Name}");//,Msg:{user.Value.Msg}
}
});
结果:

从上图我们看到在200个并行过程中,只有4个对象被使用。因此可以看出我们没有频繁的创建对象。
欢迎加入QQ群:478399354 ,到这里我们互为师长项目学习。

Anno开源地址:
AnnoGitHub源码:https://github.com/duyanming/Anno.Core
AnnoGitee源码:https://gitee.com/dotnetchina/anno.core
Viper示例项目:https://github.com/duyanming/Viper
体验地址:http://140.143.207.244/Home/Login
文档地址:https://duyanming.github.io/
关于Anno的更多内容,随后更新。敬请关注。开源不易,感谢Star。
设计一个对象池(Anno.XObjectPool)的更多相关文章
- swift实现一个对象池
1.创建一个对象池 对象池:对象池一般用来管理一组可重用的对象, 这些对象的集合叫做对象池. 组件可以从对象池中借用对象, 完成一些任务之后将它归还给对象池. 返回的对象用于满足调用组件的后续请求, ...
- 前端通信:ajax设计方案(八)--- 设计请求池,复用请求,让前端通信快、更快、再快一点
直接进入主题,本篇文章有点长,包括从设计阶段,到摸索阶段,再到实现阶段,最后全面覆盖测试阶段(包括数据搜集清洗),还有与主流前端通信框架进行对比PK阶段. 首先介绍一下一些概念: 1. 浏览器的并发能 ...
- php 设计数据库连接池
摘要 之前总是以脚本面向过程的方式写PHP代码,所以很大程度上来说,既不规范,也不安全,更不容易维护.为了代码的重用,准备写一套自己的工具库,这样的话,以后写项目的时候就可以很轻松的进行使用啦. 今天 ...
- java设计思想-池化-手写数据库连接池
https://blog.csdn.net/qq_16038125/article/details/80180941 池:同一类对象集合 连接池的作用 1. 资源重用 由于数据库连接得到重用,避免了 ...
- java对象池commons-pool-1.6详解(一)
自己的项目中用到了 对象池 commons-pool: package com.sankuai.qcs.regulation.protocol.client; import com.dianping. ...
- netty源码分析 - Recycler 对象池的设计
目录 一.为什么需要对象池 二.使用姿势 2.1 同线程创建回收对象 2.2 异线程创建回收对象 三.数据结构 3.1 物理数据结构图 3.2 逻辑数据结构图(重要) 四.源码分析 4.2.同线程获取 ...
- 抓到 Netty 一个隐藏很深的内存泄露 Bug | 详解 Recycler 对象池的精妙设计与实现
欢迎关注公众号:bin的技术小屋,如果大家在看文章的时候发现图片加载不了,可以到公众号查看原文 本系列Netty源码解析文章基于 4.1.56.Final版本 最近在 Review Netty 代码的 ...
- Linux编程之内存池的设计与实现(C++98)
假设服务器的硬件资源"充裕",那么提高服务器性能的一个很直接的方法就是空间换时间,即"浪费"服务器的硬件资源,以换取其运行效率.提升服务器性能的一个重要方法就是 ...
- Spring线程池配置模板设计(基于Springboot)
目录 线程池配置模板 基础的注解解释 常用配置参数 配置类设计 线程池使用 ThreadPoolTaskExecutor源码 线程池配置模板 springboot给我们提供了一个线程池的实现,它的底层 ...
随机推荐
- python常用数据处理库
Python之所以能够成为数据分析与挖掘领域的最佳语言,是有其独特的优势的.因为他有很多这个领域相关的库可以用,而且很好用,比如Numpy.SciPy.Matploglib.Pandas.Scikit ...
- IntelliJ IDEA安装lombok
1. 搜索Plugins 点击下方的Browse repositories.. 2.点击安装,重新启动
- VS2008开发WinCE程序编译速度慢的解决办法
1.找到以下文件 C:\Windows\Microsoft.NET\Framework\v3.5\Microsoft.CompactFramework.Common.targets 2.用记事本打开该 ...
- C语言宏的使用
使用条件宏进行条件编译 譬如,对于同一份代码,我想编译出两个不同的版本,在其中一个版本中去掉某一部分功能, 这时可以通过条件宏判断是否编译,例: 如果不使用条件宏进行控制,想编译两个不同版本的程序,就 ...
- 【MCU】国民N32固件库移植
目录 前言 移植N32Gxxx系列要点 前言 链接: 李柱明博客 移植AT32库&FreeRTOS教程 由于大部分国产MCU移植固件库.RTOS源码都是差不多的,所以本文不讲细节,如想熟悉移植 ...
- 201871030126-王会娟 实验二 个人项目—《D{0-1} KP》项目报告
项目 内容 课程班级博客链接 https://home.cnblogs.com/u/wanghuijuan815 这个作业要求链接 https://www.cnblogs.com/nwnu-daizh ...
- Spring(七篇)
(一)Spring 概述 (二)Spring Bean入门介绍 (三)Spring Bean继续入门 (四)Spring Bean注入方试 (五)Spring AOP简述 (六)Spring AOP切 ...
- canvas判断点是否在路径内
应用场景 我们的项目中有个功能是,canvas上的某个图片选中后可以再这个图片上用鼠标拖拽绘制画笔线条. 当然绘制的边界要控制在图片大小范围内的,那么鼠标是可以随意动的,怎么能控制只在图片上的时候才绘 ...
- 前端 JS 原生 javascript 和 location.hash 实现一个单页应用的路由 router
开篇日常立个flag-- 前言 最近在做一些应用,类似于单页应用,想实现类似于 Vue 路由的效果. 但是个人 Vue 基础四舍五入约等于无,而且看着 Vue-router 吃力+用不起来(因为我的项 ...
- 多图详解 TCP 连接管理,太全了!!!
TCP 是一种面向连接的单播协议,在 TCP 中,并不存在多播.广播的这种行为,因为 TCP 报文段中能明确发送方和接受方的 IP 地址. 在发送数据前,相互通信的双方(即发送方和接受方)需要建立一条 ...