.NET Core中Object Pool的简单使用

前言

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

举个最为简单,你们最为熟悉的例子,数据库链接池,就是复用数据库链接。github

那么复用的意义在那里呢?数据库

简单来讲就是减小没必要要的资源损耗。ide

除了数据库链接,可能在不一样的情景或需求下,还会有不少其余对象须要进行复用,这个时候就会有所谓的 Object Pool(对象池)。函数

小伙伴们应该也本身实现过相似的功能,或用ConcurrentBag,或用ConcurrentQueue,或用其余方案。ui

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

How to: Create an Object Pool by Using a ConcurrentBag3d

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

咱们只须要添加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

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

相关文章
相关标签/搜索