复用,是一个重要的话题,也是咱们平常开发中常常遇到的,不可避免的问题。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能够说是.NET Core的一个基础类库。
它位于aspnet的Common项目中,类型其余基础模块都有使用相关的功能,也比如Routing项目。
下面就简单看看它的用法。
在开始以前,咱们先定义一个能够复用的object
public class Demo { public int Id { get; set; } public string Name { get; set; } public DateTime CreateTimte { get; set; } }
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
先定义一个Policy,实现 IPooledObjectPolicy
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并非同一个。
除了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,本质是同样的。
好像上面的用法,都不那么像咱们正常使用的。咱们仍是须要依赖注入的。
那么咱们最后就来看看怎么结合依赖注入吧。固然这里的本质仍是离不开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
最后用一个脑图结束本文。