.NET Core中Object Pool的多种用法详解

前言

复用,是一个重要的话题,也是我们日常开发中经常遇到的,不可避免的问题。

举个最为简单,大家最为熟悉的例子,数据库连接池,就是复用数据库连接。

那么复用的意义在那里呢?

简单来说就是减少不必要的资源损耗。

除了数据库连接,可能在不同的情景或需求下,还会有很多其他对象需要进行复用,这个时候就会有所谓的 Object Pool(对象池)。

小伙伴们应该也自己实现过类似的功能,或用ConcurrentBag,或用ConcurrentQueue,或用其他方案。

这也里分享一个在微软文档中的实现

How to: Create an Object Pool by Using a ConcurrentBag

当然,在.NET Core中,微软已经帮我们实现了一个简单的Object Pool。

我们只需要添加Microsoft.Extensions.ObjectPool的引用即可使用了。

Microsoft.Extensions.ObjectPool

Microsoft.Extensions.ObjectPool可以说是.NET Core的一个基础类库。

它位于aspnet的Common项目中,类型其他基础模块都有使用相关的功能,也好比Routing项目。

下面就简单看看它的用法。

在开始之前,我们先定义一个可以复用的object

public class Demo
{
 public int Id { get; set; }
 public string Name { get; set; }
 public DateTime CreateTimte { get; set; }
}

用法1

var defalutPolicy = new DefaultPooledObjectPolicy<Demo>();
//最大可保留对象数量 = Environment.ProcessorCount * 2
var defaultPool = new DefaultObjectPool<Demo>(defalutPolicy);
for (int i = 0; i < PoolSize; i++)
{
 item1 = defaultPool.Get();
 Console.WriteLine($"#{i+1}-{item1.Id}-{item1.Name}-{item1.CreateTimte}");
}

在创建pool之前,我们要先定义一个Policy。这里直接用自带的DefaultPooledObjectPolicy来构造。

对象池会有一个维护的最大数量,线程数。

通过pool对象的Get方法,从对象池中取出一个对象。

上面代码运行结果

#1-0--01/01/0001 00:00:00
#2-0--01/01/0001 00:00:00
#3-0--01/01/0001 00:00:00
#4-0--01/01/0001 00:00:00
#5-0--01/01/0001 00:00:00
#6-0--01/01/0001 00:00:00
#7-0--01/01/0001 00:00:00
#8-0--01/01/0001 00:00:00

这个结果说明,Object Pool 中的对象都是直接new出来的,并没有对一些属性进行贬值操作,这个时候往往没有太多实际意义。

因为DefaultPooledObjectPolicy本来就是直接new了一个对象出来,很多时候,这并不是我们所期望的!

要想符合我们实际的使用,就要自己定义一个Policy!

下面来看看用法2

用法2

先定义一个Policy,实现 IPooledObjectPolicy 这个接口。T很自然就是我们的Demo类了。

public class DemoPooledObjectPolicy : IPooledObjectPolicy<Demo>
{
 public Demo Create()
 {
  return new Demo { Id = 1, Name = "catcher", CreateTimte = DateTime.Now };
 }

 public bool Return(Demo obj)
 {
  return true;
 }
}

这里要实现Create和Return两个方法。

见名知义,Create方法就是用来创建Demo对象的,Return方法就是将Demo对象扔回Object Pool的(有借有还)。

然后是用DemoPooledObjectPolicy去替换DefaultPooledObjectPolicy。

var demoPolicy = new DemoPooledObjectPolicy();
var defaultPoolWithDemoPolicy = new DefaultObjectPool<Demo>(demoPolicy,1);
//借
item1 = defaultPoolWithDemoPolicy.Get();
//还
defaultPoolWithDemoPolicy.Return(item1);
//借,但是不还
item2 = defaultPoolWithDemoPolicy.Get();
Console.WriteLine($"{item1.Id}-{item1.Name}-{item1.CreateTimte}");
Console.WriteLine($"{item2.Id}-{item2.Name}-{item2.CreateTimte}");
Console.WriteLine(item1 == item2);
//创建一个新的
item3 = defaultPoolWithDemoPolicy.Get();
Console.WriteLine($"{item3.Id}-{item3.Name}-{item3.CreateTimte}");
Console.WriteLine(item3 == item1);

这里定义了对象池只保留一个对象。

由于从object pool中取出来之后,有一步还回去的操作,所以item1和item2应当是同一个对象。

从object pool中拿出了item2之后,它并没有还回去,所以object pool会基于我们定义的Policy去创建一个新的对象出来。

下面是用法2的输出结果:

1-catcher-09/17/2018 22:32:38
1-catcher-09/17/2018 22:32:38
True
1-catcher-09/17/2018 22:32:38
False

可以看到item1,item2和item3的各个属性是一样的,并且item1和item2确实是同一个对象。item3和item1并不是同一个。

用法3

除了DefaultObjectPool外,还有DefaultObjectPoolProvider也可以创建一个Object Pool。

创建一个Object Pool,一定是离不开Policy的,所以这里还是用了我们自己定义的DemoPooledObjectPolicy。

var defaultProvider = new DefaultObjectPoolProvider();
var policy = new DemoPooledObjectPolicy();
//default maximumRetained is Environment.ProcessorCount * 2
ObjectPool<Demo> pool = defaultProvider.Create(policy);
item1 = pool.Get();
pool.Return(item1);
item2 = pool.Get();
Console.WriteLine($"{item1.Id}-{item1.Name}-{item1.CreateTimte}");
Console.WriteLine($"{item2.Id}-{item2.Name}-{item2.CreateTimte}");
Console.WriteLine(item1 == item2);
item3 = pool.Get();
Console.WriteLine($"{item3.Id}-{item3.Name}-{item3.CreateTimte}");
Console.WriteLine(item3 == item2);

用Provider创建Object Pool时,不能指定保留的最大对象数量,只能用的是默认的Environment.ProcessorCount * 2。

后面的使用,和用法2是一样的。

可以看到item1和item2是同一个对象。从Object Pool中取对象的时候,会取第一个,所以还回去后,再取的话,还是会取到原来的第一个。

item3那里是直接从Object Pool中取出来的,没有再次创建,因为这里的Object Pool维护着多个对象,而不是用法2中的只有一个,所以它是直接从Pool中拿的。

下面是输出结果

1-catcher-09/17/2018 22:38:34
1-catcher-09/17/2018 22:38:34
True
1-catcher-09/17/2018 22:38:34
False

和用法2,本质是一样的。

用法4

好像上面的用法,都不那么像我们正常使用的。我们还是需要依赖注入的。

那么我们最后就来看看怎么结合依赖注入吧。当然这里的本质还是离不开Policy和Provider这两个东西。

IServiceCollection services = new ServiceCollection();
services.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
services.AddSingleton(s =>
{
 var provider = s.GetRequiredService<ObjectPoolProvider>();
 return provider.Create(new DemoPooledObjectPolicy());
});
ServiceProvider serviceProvider = services.BuildServiceProvider();
var pool = serviceProvider.GetService<ObjectPool<Demo>>();
item1 = pool.Get();
pool.Return(item1);
item2 = pool.Get();
Console.WriteLine($"{item1.Id}-{item1.Name}-{item1.CreateTimte}");
Console.WriteLine($"{item2.Id}-{item2.Name}-{item2.CreateTimte}");
Console.WriteLine(item1 == item2);
item3 = pool.Get();
Console.WriteLine($"{item3.Id}-{item3.Name}-{item3.CreateTimte}");
Console.WriteLine(item3 == item2);

我们首先需要完成对Provider的注册,然后直接拿它的实例去创建一个Object Pool即可。

如果想在其他地方用,通过构造函数注入即可。

这里的结果也是和前面一样的,没什么好多说的。

总结

在这几种用法中,我们最常用的应该是用法4。

但是无论那种用法,我们都需要了解,Object Pool离不开Pool,Policy和Provider这三个家伙。

有了这三个,或许我们就可以为所欲为了。

当然,它还提供了几个特殊的东西,有兴趣的可以去看看。

•LeakTrackingObjectPool

•StringBuilderPooledObjectPolicy

最后用一个脑图结束本文。

以上所述是小编给大家介绍的.NET Core中Object Pool的多种用法详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • .NET Core中使用HttpClient的正确姿势

    前言 为了更方便在服务端调用 HTTP 请求,微软在 .NET Framework 4.x 的时候引入了 HttpClient.但 HttpClient 有很多严重问题,一直饱受诟病,比如 InfoQ 的这篇文章 t.cn/Evzy80y,吐槽了 HttpClient 不能立即关闭连接.性能消耗严重等的问题. Http协议的重要性相信不用我多说了,HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性,它不仅是客户端发送Http请求变得容易,而且也方便了开发人员

  • .net core 读取本地指定目录下的文件的实例代码

    项目需求 asp.net core 读取log目录下的.log文件,.log文件的内容如下: xxx.log ------------------------------------------begin--------------------------------- 写入时间:2018-09-11 17:01:48  userid=1000  golds=10  -------------------------------------------end-------------------

  • .Net Core中间件之静态文件(StaticFiles)示例详解

    一.介绍 静态文件(static files),诸如 HTML.CSS.图片和 JavaScript 之类的资源会被 ASP.NET Core 应用直接提供给客户端. 在介绍静态文件中间件之前,先介绍 ContentRoot和WebRoot概念. ContentRoot:指web的项目的文件夹,包括bin和webroot文件夹. WebRoot:一般指ContentRoot路径下的wwwroot文件夹. 介绍这个两个概念是因为静态资源文件一般存放在WebRoot路径下,也就是wwwroot.下面

  • asp.net core webapi项目配置全局路由的方法示例

    一.前言 在开发项目的过程中,我新创建了一个controller,发现vs会给我们直接在controller头添加前缀,比如[Route("api/[controller]")],即在访问接口的时候会变成http://localhost:8000/api/values,但是如果控制器有很多个,或者要进行版本迭代时,我们会发现痛苦的时刻降临了,要一个一个的修改. 如果在这个时候可以进行全局配置前缀那真是福利呀,修改一处即可.为了能达到此目的我们就来运用一下吧. 二.配置 0.在配置前我们

  • Asp.Net Core中基于Session的身份验证的实现

    在Asp.Net框架中提供了几种身份验证方式:Windows身份验证.Forms身份验证.passport身份验证(单点登录验证). 每种验证方式都有适合它的场景: 1.Windowss身份验证通常用于企业内部环境,Windows Active Directory就是基于windows平台的身份验证实现: 2.Forms身份验证是Asp.Net框架中提出的另一种验证方式: 3.passport身份验证是微软提供的基于自己的lives账号实现的单点认证服务. Asp.net Core验证码登录遇到

  • .Net Core中使用Quartz.Net实践记录

    一.介绍 Quartz.NET是一个强大.开源.轻量的作业调度框架,是 OpenSymphony 的 Quartz API 的.NET移植,用C#改写,可用于winform和asp.net应用中.它灵活而不复杂.你能够用它来为执行一个作业而创建简单的或复杂的作业调度.它有很多特征,如:数据库支持,集群,插件,支持cron-like表达式等等. 通俗说它的功能是:比如说我想每天晚上2点让程序或网站执行某些代码,或者每隔5秒种我想查看是否有新的任务要处理等. Quartz.Net是根据Java的Qu

  • 详解.NET Core中的数据保护组件

    背景介绍 在 OWASP(开放式 Web 应用程序安全项目) 2013 年发布的报告中,将不安全的直接对象引用(Insecure Direct Object Reference)标记为 十大 Web 应用程序风险之一, 其表现形式是对象的引用(例如数据库主键)被各种恶意攻击利用, 所以对于Api返回的各种主键外键ID, 我们需要进行加密. .NET Core 的数据保护组件 .NET Core 中内置了一个IDataProtectionProvider接口和一个IDataProtector接口.

  • .NET Core中Object Pool的多种用法详解

    前言 复用,是一个重要的话题,也是我们日常开发中经常遇到的,不可避免的问题. 举个最为简单,大家最为熟悉的例子,数据库连接池,就是复用数据库连接. 那么复用的意义在那里呢? 简单来说就是减少不必要的资源损耗. 除了数据库连接,可能在不同的情景或需求下,还会有很多其他对象需要进行复用,这个时候就会有所谓的 Object Pool(对象池). 小伙伴们应该也自己实现过类似的功能,或用ConcurrentBag,或用ConcurrentQueue,或用其他方案. 这也里分享一个在微软文档中的实现 Ho

  • Django中的FBV和CBV用法详解

    FBV FBV,即 func base views,函数视图,在视图里使用函数处理请求. 以用户注册代码为例, 使用两个函数完成注册 初级注册代码 def register(request): """返回注册页面""" return render(request, "register.html") def register_handle(request): """进行注册处理""

  • Java开发中synchronized的定义及用法详解

    概念 是利用锁的机制来实现同步的. 互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程中的协调机制,这样在同一时间只有一个线程对需同步的代码块(复合操作)进行访问.互斥性我们也往往称为操作的原子性. 可见性:必须确保在锁被释放之前,对共享变量所做的修改,对于随后获得该锁的另一个线程是可见的(即在获得锁时应获得最新共享变量的值),否则另一个线程可能是在本地缓存的某个副本上继续操作从而引起不一致. 用法 修饰静态方法: //同步静态方法 public synchronized

  • JSP 中request与response的用法详解

    JSP 中request与response的用法详解 概要: 在学习这两个对象之前,我们应该已经有了http协议的基本了解了,如果不清楚http协议的可以看我的关于http协议的介绍.因为其实request和response的使用大部分都是对http协议的操作. request对象的介绍 我们先从request对象进行介绍: 我们知道http协议定义了请求服务器的格式: 请求行 请求头 空格 请求体(get请求没有请求体) 好了,这里我们就不详细介绍了,我们只看几个应用就可以了,没什么难度: 应

  • Python中flatten( )函数及函数用法详解

    flatten()函数用法 flatten是numpy.ndarray.flatten的一个函数,即返回一个一维数组. flatten只能适用于numpy对象,即array或者mat,普通的list列表不适用!. a.flatten():a是个数组,a.flatten()就是把a降到一维,默认是按行的方向降 . a.flatten().A:a是个矩阵,降维后还是个矩阵,矩阵.A(等效于矩阵.getA())变成了数组.具体看下面的例子: 1.用于array(数组)对象 >>> from n

  • python爬虫中的url下载器用法详解

    前期的入库筛选工作已经由url管理器完成了,整理的工作自然要由url下载器接手.当我们需要爬取的数据已经去重后,下载器的主要任务的是这些数据下载下来.所以它的使用也并不复杂,不过需要借助到我们之前所学过的一个库进行操作,相信之前的基础大家都学的很牢固.下面小编就来为大家介绍url下载器及其使用的方法. 下载器的作用就是接受URL管理器传递给它的一个url,然后把该网页的内容下载下来.python自带有urllib和urllib2等库(这两个库在python3中合并为urllib),它们的作用就是

  • C++中TinyXML读取xml文件用法详解

    目录 前言 XML文件理解 常用的XML类方法使用 总结 前言 TinyXML下载地址:https://sourceforge.net/projects/tinyxml/ 官方文档:TinyXML TinyXML是个解析库,它由两个头文件(.h文件)和四个CPP文件(.cpp文件)构成,用的时候,只要将(tinyxml.h.tinystr.h.tinystr.cpp.tinyxml.cpp.tinyxmlerror.cpp.tinyxmlparser.cpp)导入工程就可以用它的东西了.如果需要

  • Python pandas中apply函数简介以及用法详解

    目录 1.基本信息 2.语法结构 3.使用案例 3.1 DataFrame使用apply 3.2 Series使用apply 3.3 其他案例 4.总结 参考链接: 1.基本信息 ​ Pandas 的 apply() 方法是用来调用一个函数(Python method),让此函数对数据对象进行批量处理.Pandas 的很多对象都可以使用 apply() 来调用函数,如 Dataframe.Series.分组对象.各种时间序列等. 2.语法结构 ​ apply() 使用时,通常放入一个 lambd

  • IOS开发中NSURL的基本操作及用法详解

    NSURL其实就是我们在浏览器上看到的网站地址,这不就是一个字符串么,为什么还要在写一个NSURL呢,主要是因为网站地址的字符串都比较复杂,包括很多请求参数,这样在请求过程中需要解析出来每个部门,所以封装一个NSURL,操作很方便. 1.URL URL是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址.互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它. URL可能包含远程服务器上的资源的位置,本地磁盘上的文件的路径,甚

  • JavaScript中SetInterval与setTimeout的用法详解

    setTimeout 描述 setTimeout(code,millisec) setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式. 注:调用过程中,可以使用clearTimeout(id_of_settimeout)终止 参数 描述 code 必需,要调用的函数后要执行的 JavaScript 代码串. millisec 必需,在执行代码前需等待的毫秒数. setTimeinterval setInterval(code,millisec[,"lang"]) 参数

随机推荐