複用,是一個重要的話題,也是咱們平常開發中常常遇到的,不可避免的問題。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
最後用一個腦圖結束本文。