设计一个对象池(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给我们提供了一个线程池的实现,它的底层 ...
随机推荐
- MySQL入门(7)——表数据的增、删、改
MySQL入门(7)--表数据的增.删.改 插入数据 使用INSERT···VALUES语句插入数据 INSERT语句最常用的格式是INSERT···VALUES: INSERT [LOW_PRIOR ...
- 某SQL注入--报错注入payload
1.证明存在sql注入,根据这个报错语句,,有' 有% 2.payload 闭合语句 %' or (select extractvalue("anything",concat( ...
- c# DataGirdView动态刷新
using MySql.Data.MySqlClient;using System; using System.Data; using System.Threading; using System.W ...
- java例题_25 判断是否为回文数!
1 /*25 [程序 25 求回文数] 2 题目:一个 5 位数,判断它是不是回文数.即 12321 是回文数,个位与万位相同,十位与千位相同. 3 */ 4 5 /*分析 6 * 先用%和/将5个数 ...
- java例题_18 乒乓球比赛(对手问题)
1 /*18 [程序 18 乒乓球赛] 2 题目:两个乒乓球队进行比赛,各出三人.甲队为 a,b,c 三人,乙队为 x,y,z 三人.已抽签决定比赛名单. 3 有人向队员打听比赛的名单.a说他不和 x ...
- TreeMap和HashMap的元素比较
写在前面的话 2021.04,准备面试和CCF CSP认证的我准备做一套CCF模拟题,然后就有了此篇博客(x 题目:201912-2 回收站报数 题目截图: 第一个想法:读取每个垃圾的位置,存入Tre ...
- 一文带大家彻底搞懂Hystrix!
前言? Netflix Hystrix断路器是什么? Netflix Hystrix是SOA/微服务架构中提供服务隔离.熔断.降级机制的工具/框架.Netflix Hystrix是断路器的一种实现,用 ...
- 鸿蒙开源第三方组件——SlidingMenu_ohos侧滑菜单组件
目录: 1.前言 2.背景 3.效果展示 4.Sample解析 5.Library解析 6.<鸿蒙开源第三方组件>文章合集 前言 基于安卓平台的SlidingMenu侧滑菜单组件(http ...
- 【算法学习笔记】组合数与 Lucas 定理
卢卡斯定理是一个与组合数有关的数论定理,在算法竞赛中用于求组合数对某质数的模. 第一部分是博主的个人理解,第二部分为 Pecco 学长的介绍 第一部分 一般情况下,我们计算大组合数取模问题是用递推公式 ...
- Spring MVC(七篇)
(一)Spring MVC简介 (二)SpringMVC核心控制器 (三)Spring MVC Controller接口控制器详解(一) (三)Spring MVC Controller接口控制器详解 ...