设计一个.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)的更多相关文章

  1. swift实现一个对象池

    1.创建一个对象池 对象池:对象池一般用来管理一组可重用的对象, 这些对象的集合叫做对象池. 组件可以从对象池中借用对象, 完成一些任务之后将它归还给对象池. 返回的对象用于满足调用组件的后续请求, ...

  2. 前端通信:ajax设计方案(八)--- 设计请求池,复用请求,让前端通信快、更快、再快一点

    直接进入主题,本篇文章有点长,包括从设计阶段,到摸索阶段,再到实现阶段,最后全面覆盖测试阶段(包括数据搜集清洗),还有与主流前端通信框架进行对比PK阶段. 首先介绍一下一些概念: 1. 浏览器的并发能 ...

  3. php 设计数据库连接池

    摘要 之前总是以脚本面向过程的方式写PHP代码,所以很大程度上来说,既不规范,也不安全,更不容易维护.为了代码的重用,准备写一套自己的工具库,这样的话,以后写项目的时候就可以很轻松的进行使用啦. 今天 ...

  4. java设计思想-池化-手写数据库连接池

     https://blog.csdn.net/qq_16038125/article/details/80180941 池:同一类对象集合 连接池的作用 1. 资源重用 由于数据库连接得到重用,避免了 ...

  5. java对象池commons-pool-1.6详解(一)

    自己的项目中用到了 对象池 commons-pool: package com.sankuai.qcs.regulation.protocol.client; import com.dianping. ...

  6. netty源码分析 - Recycler 对象池的设计

    目录 一.为什么需要对象池 二.使用姿势 2.1 同线程创建回收对象 2.2 异线程创建回收对象 三.数据结构 3.1 物理数据结构图 3.2 逻辑数据结构图(重要) 四.源码分析 4.2.同线程获取 ...

  7. 抓到 Netty 一个隐藏很深的内存泄露 Bug | 详解 Recycler 对象池的精妙设计与实现

    欢迎关注公众号:bin的技术小屋,如果大家在看文章的时候发现图片加载不了,可以到公众号查看原文 本系列Netty源码解析文章基于 4.1.56.Final版本 最近在 Review Netty 代码的 ...

  8. Linux编程之内存池的设计与实现(C++98)

    假设服务器的硬件资源"充裕",那么提高服务器性能的一个很直接的方法就是空间换时间,即"浪费"服务器的硬件资源,以换取其运行效率.提升服务器性能的一个重要方法就是 ...

  9. Spring线程池配置模板设计(基于Springboot)

    目录 线程池配置模板 基础的注解解释 常用配置参数 配置类设计 线程池使用 ThreadPoolTaskExecutor源码 线程池配置模板 springboot给我们提供了一个线程池的实现,它的底层 ...

随机推荐

  1. PAT (Advanced Level) Practice 1011 World Cup Betting (20 分) 凌宸1642

    PAT (Advanced Level) Practice 1011 World Cup Betting (20 分) 凌宸1642 题目描述: With the 2010 FIFA World Cu ...

  2. Cloudreve 自建云盘实践,我说了没人能限得了我的容量和速度!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 为啥要用自建网盘,市面上的云盘不香了? 每一个用户需求的背后都是因为有场景存在,而这 ...

  3. Dynamics CRM9.0安装CRM的时候提示Microsoft.Crm.Setup.Server.RegisterSandboxServiceAction操作失败

    如图: 这种问题有两种情况会遇到.第一种是一个域装多套CRM产品会提示这个问题这个问题是解决不了的. 还一种情况就是安装9.0及以后的产品先安装了ADFS再安装产品就会提示这个错误. 第二种解决办法就 ...

  4. JavaWeb 补充(Json)

    HTML DOM alert() 方法 定义和用法 alert() 方法用于显示带有一条指定消息和一个 OK 按钮的警告框. 参数 描述 message 要在 window 上弹出的对话框中显示的纯文 ...

  5. 数据库MySQL五

    测试题复习 子查询案例 DML语句(很重要) 自增长列 为某一个字段设置自增长 修改语句 truncate实际上是DDL语句删除表再新建一个表 DCL事务 ACID 回滚:没发生 提交才更新数据 /* ...

  6. Java(246-264)【List、Set】

    1.数据结构_栈 Stack先进后出 2.数据结构_队列 Queue先进先出 3.数据结构_数组 Array查询快.增删慢 需要创建一个新数组,将指定新元素存储在指定索引位置,再把原数组元素根 据索引 ...

  7. [源码分析] 分布式任务队列 Celery 之 发送Task & AMQP

    [源码分析] 分布式任务队列 Celery 之 发送Task & AMQP 目录 [源码分析] 分布式任务队列 Celery 之 发送Task & AMQP 0x00 摘要 0x01 ...

  8. Linux 基础命令 命令进阶

    Linux命令格式:命令 选项 参数 (大部分命令是这个格式) 注意: 1.命令区分大小写 2.短选项可以合并   长选项不能合并 如 : 短选项 -l  -h 可以合并为 -lh 长选项  不能合并 ...

  9. Day06_27_多态

    多态 最关键一句话: 父类引用 指向(=) 子类对象 Animal a = new Cat(); 什么是多态? 官方说: 接口的多种不同的实现方式即为多态. 多态性是允许你将父对象设置成为一个或更多的 ...

  10. JVM JIT动态编译

    一.概述 1.1 基本概念 a. 动态编译(dynamic compilation)指的是"在运行时进行编译":与之相对的是事前编译(ahead-of-time compilati ...