.NET Core對(duì)象池的應(yīng)用:設(shè)計(jì)篇
《編程篇》已經(jīng)涉及到了對(duì)象池模型的大部分核心接口和類型。對(duì)象池模型其實(shí)是很簡(jiǎn)單的,不過(guò)其中有一些為了提升性能而刻意為之的實(shí)現(xiàn)細(xì)節(jié)倒是值得我們關(guān)注。總的來(lái)說(shuō),對(duì)象池模型由三個(gè)核心對(duì)象構(gòu)成,它們分別是表示對(duì)象池的ObjectPool<T>對(duì)象、對(duì)象值提供者的ObjectPoolProvider對(duì)象,已及控制池化對(duì)象創(chuàng)建與釋放行為的IPooledObjectPolicy<T>對(duì)象,我們先來(lái)介紹最后一個(gè)對(duì)象。
一、 IPooledObjectPolicy<T>
我們?cè)凇?a target="_blank" href="http://www.dbjr.com.cn/article/221512.htm">編程篇》已經(jīng)說(shuō)過(guò),表示池化對(duì)象策略的IPooledObjectPolicy<T>對(duì)象不僅僅幫助我們創(chuàng)建對(duì)象,還可以幫助我們執(zhí)行一些對(duì)象回歸對(duì)象池之前所需的回收操作,對(duì)象最終能否回到對(duì)象池中也受它的控制。如下面的代碼片段所示,IPooledObjectPolicy<T>接口定義了兩個(gè)方法,Create方法用來(lái)創(chuàng)建池化對(duì)象,對(duì)象回歸前需要執(zhí)行的操作體現(xiàn)在Return方法上,該方法的返回值決定了指定的對(duì)象是否應(yīng)該回歸對(duì)象池。抽象類PooledObjectPolicy<T>實(shí)現(xiàn)了該接口,我們一般將它作為自定義策略類型的基類。
public interface IPooledObjectPolicy<T> { T Create(); bool Return(T obj); } public abstract class PooledObjectPolicy<T> : IPooledObjectPolicy<T> { protected PooledObjectPolicy(){} public abstract T Create(); public abstract bool Return(T obj); }
我們默認(rèn)使用的是如下這個(gè)DefaultPooledObjectPolicy<T>類型,由于它直接通過(guò)反射來(lái)創(chuàng)建池化對(duì)象,所以要求泛型參數(shù)T必須有一個(gè)公共的默認(rèn)無(wú)參構(gòu)造函數(shù)。它的Return方法直接返回True,意味著提供的對(duì)象可以被無(wú)限制地復(fù)用。
public class DefaultPooledObjectPolicy<T> : PooledObjectPolicy<T> where T: class, new() { public override T Create() => Activator.CreateInstance<T>(); public override bool Return(T obj) => true; }
二、ObjectPool<T>
對(duì)象池通過(guò)ObjectPool<T>對(duì)象表示。如下面的代碼片段所示,ObjectPool<T>是一個(gè)抽象類,池化對(duì)象通過(guò)Get方法提供給我們,我們?cè)谑褂猛曛笳{(diào)用Return方法將其釋放到對(duì)象池中以供后續(xù)復(fù)用。
public abstract class ObjectPool<T> where T: class { protected ObjectPool(){} public abstract T Get(); public abstract void Return(T obj); }
DefaultObjectPool<T>
我們默認(rèn)使用的對(duì)象池體現(xiàn)為一個(gè)DefaultObjectPool<T>對(duì)象,由于針對(duì)對(duì)象池的絕大部分實(shí)現(xiàn)就體現(xiàn)這個(gè)類型中,所以它也是本節(jié)重點(diǎn)講述的內(nèi)容。我們?cè)谇懊嬉还?jié)已經(jīng)說(shuō)過(guò),對(duì)象池具有固定的大小,并且默認(rèn)的大小為處理器個(gè)數(shù)的2倍。我們假設(shè)對(duì)象池的大小為N,那么DefaultObjectPool<T>對(duì)象會(huì)如下圖所示的方式使用一個(gè)單一對(duì)象和一個(gè)長(zhǎng)度為N-1的數(shù)組來(lái)存放由它提供的N個(gè)對(duì)象。
如下面的代碼片段所示,DefaultObjectPool<T>使用字段_firstItem用來(lái)存放第一個(gè)池化對(duì)象,余下的則存放在_items字段表示的數(shù)組中。值得注意的是,這個(gè)數(shù)組的元素類型并非池化對(duì)象的類型T,而是一個(gè)封裝了池化對(duì)象的結(jié)構(gòu)體ObjectWrapper。如果該數(shù)組元素類型改為引用類型T,那么當(dāng)我們對(duì)某個(gè)元素進(jìn)行復(fù)制的時(shí)候,運(yùn)行時(shí)會(huì)進(jìn)行類型校驗(yàn)(要求指定對(duì)象類型派生于T),無(wú)形之中帶來(lái)了一定的性能損失(值類型數(shù)組就不需求進(jìn)行派生類型的校驗(yàn))。我們?cè)谇懊嫣岬竭^(guò),對(duì)象池中存在一些性能優(yōu)化的細(xì)節(jié),這就是其中之一。
public class DefaultObjectPool<T> : ObjectPool<T> where T : class { private protected T _firstItem; private protected readonly ObjectWrapper[] _items; … private protected struct ObjectWrapper { public T Element; } }
DefaultObjectPool<T>類型定義了如下兩個(gè)構(gòu)造函數(shù)。我們?cè)趧?chuàng)建一個(gè)DefaultObjectPool<T>對(duì)象的時(shí)候會(huì)提供一個(gè)IPooledObjectPolicy<T>對(duì)象并指定對(duì)象池的大小。對(duì)象池的大小默認(rèn)設(shè)置為處理器數(shù)量的2倍體現(xiàn)在第一個(gè)構(gòu)造函數(shù)重載中。如果指定的是一個(gè)DefaultPooledObjectPolicy<T>對(duì)象,表示默認(rèn)池化對(duì)象策略的_isDefaultPolicy字段被設(shè)置成True。因?yàn)镈efaultPooledObjectPolicy<T>對(duì)象的Return方法總是返回True,并且沒(méi)有任何具體的操作,所以在將對(duì)象釋放回對(duì)象池的時(shí)候就不需要調(diào)用Return方法了,這是第二個(gè)性能優(yōu)化的細(xì)節(jié)。
public class DefaultObjectPool<T> : ObjectPool<T> where T : class { private protected T _firstItem; private protected readonly ObjectWrapper[] _items; private protected readonly IPooledObjectPolicy<T> _policy; private protected readonly bool _isDefaultPolicy; private protected readonly PooledObjectPolicy<T> _fastPolicy; public DefaultObjectPool(IPooledObjectPolicy<T> policy) : this(policy, Environment.ProcessorCount * 2) {} public DefaultObjectPool(IPooledObjectPolicy<T> policy, int maximumRetained) { _policy = policy ; _fastPolicy = policy as PooledObjectPolicy<T>; _isDefaultPolicy = IsDefaultPolicy(); _items = new ObjectWrapper[maximumRetained - 1]; bool IsDefaultPolicy() { var type = policy.GetType(); return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(DefaultPooledObjectPolicy<>); } } [MethodImpl(MethodImplOptions.NoInlining)] private T Create() => _fastPolicy?.Create() ?? _policy.Create(); }
從第二個(gè)構(gòu)造函數(shù)的定義可以看出,指定的IPooledObjectPolicy<T>對(duì)象除了會(huì)賦值給_policy字段之外,如果提供的是一個(gè)PooledObjectPolicy<T>對(duì)象,該對(duì)象還會(huì)同時(shí)賦值給另一個(gè)名為_(kāi)fastPolicy的字段。在進(jìn)行池化對(duì)象的提取和釋放時(shí),_fastPolicy字段表示的池化對(duì)象策略會(huì)優(yōu)先選用,這個(gè)邏輯體現(xiàn)在Create方法上。因?yàn)檎{(diào)用類型的方法比調(diào)用接口方法具有更好的性能(所以該字段才會(huì)命名為_(kāi)fastPolicy),這是第三個(gè)性能優(yōu)化的細(xì)節(jié)。這個(gè)細(xì)節(jié)還告訴我們?cè)谧远x池化對(duì)象策略的時(shí)候,最好將PooledObjectPolicy<T>作為基類,而不是直接實(shí)現(xiàn)IPooledObjectPolicy<T>接口。
如下所示的是重寫的Get和Return方法的定義。用于提供池化對(duì)象的Get方法很簡(jiǎn)單,它會(huì)采用原子操作使用Null將_firstItem字段表示的對(duì)象“替換”下來(lái),如果該字段不為Null,那么將其作為返回的對(duì)象,反之它會(huì)遍歷數(shù)組的每個(gè)ObjectWrapper對(duì)象,并使用Null將其封裝的對(duì)象“替換”下來(lái),第一個(gè)成功替換下來(lái)的對(duì)象將作為返回值。如果所有ObjectWrapper對(duì)象封裝的對(duì)象都為Null,意味著所有對(duì)象都被“借出”或者尚未創(chuàng)建,此時(shí)返回創(chuàng)建的新對(duì)象了。
public class DefaultObjectPool<T> : ObjectPool<T> where T : class { public override T Get() { var item = _firstItem; if (item == null || Interlocked.CompareExchange(ref _firstItem, null, item) != item) { var items = _items; for (var i = 0; i < items.Length; i++) { item = items[i].Element; if (item != null && Interlocked.CompareExchange( ref items[i].Element, null, item) == item) { return item; } } item = Create(); } return item; } public override void Return(T obj) { if (_isDefaultPolicy || (_fastPolicy?.Return(obj) ?? _policy.Return(obj))) { if (_firstItem != null || Interlocked.CompareExchange(ref _firstItem, obj, null) != null) { var items = _items; for (var i = 0; i < items.Length && Interlocked.CompareExchange( ref items[i].Element, obj, null) != null; ++i) {} } } } … }
將對(duì)象釋放會(huì)對(duì)象池的Return方法也很好理解。首先它需要判斷指定的對(duì)象能否釋放會(huì)對(duì)象池中,如果使用的是默認(rèn)的池化對(duì)象策略,答案是肯定的,否則只能通過(guò)調(diào)用IPooledObjectPolicy<T>對(duì)象的Return方法來(lái)判斷。從代碼片段可以看出,這里依然會(huì)優(yōu)先選擇_fastPolicy字段表示的PooledObjectPolicy<T>對(duì)象以獲得更好的性能。
在確定指定的對(duì)象可以釋放回對(duì)象之后,如果_firstItem字段為Null,Return方法會(huì)采用原子操作使用指定的對(duì)象將其“替換”下來(lái)。如果該字段不為Null或者原子替換失敗,該方法會(huì)便利數(shù)組的每個(gè)ObjectWrapper對(duì)象,并采用原子操作將它們封裝的空引用替換成指定的對(duì)象。整個(gè)方法會(huì)在某個(gè)原子替換操作成功或者整個(gè)便利過(guò)程結(jié)束之后返回。
DefaultObjectPool<T>之所有使用一個(gè)數(shù)組附加一個(gè)單一對(duì)象來(lái)存儲(chǔ)池化對(duì)象,是因?yàn)獒槍?duì)單一字段的讀寫比針對(duì)數(shù)組元素的讀寫具有更好的性能。從上面給出的代碼可以看出,不論是Get還是Return方法,優(yōu)先選擇的都是_firstItem字段。如果池化對(duì)象的使用率不高,基本上使用的都會(huì)是該字段存儲(chǔ)的對(duì)象,那么此時(shí)的性能是最高的。
DisposableObjectPool<T>
通過(guò)前面的示例演示我們知道,當(dāng)池化對(duì)象類型實(shí)現(xiàn)了IDisposable接口的情況下,如果某個(gè)對(duì)象在回歸對(duì)象池的時(shí)候,對(duì)象池已滿,該對(duì)象將被丟棄。與此同時(shí),被丟棄對(duì)象的Dispose方法將立即被調(diào)用。但是這種現(xiàn)象并沒(méi)有在DefaultObjectPool<T>類型的代碼中體現(xiàn)出來(lái),這是為什么呢?實(shí)際上DefaultObjectPool<T>還有如下這個(gè)名為DisposableObjectPool<T>的派生類。如代碼片段可以看出,表示池化對(duì)象類型的泛型參數(shù)T要求實(shí)現(xiàn)IDisposable接口。如果池化對(duì)象類型實(shí)現(xiàn)了IDisposable接口,通過(guò)默認(rèn)ObjectPoolProvider對(duì)象創(chuàng)建的對(duì)象池就是一個(gè)DisposableObjectPool<T>對(duì)象。
internal sealed class DisposableObjectPool<T> : DefaultObjectPool<T>, IDisposable where T : class { private volatile bool _isDisposed; public DisposableObjectPool(IPooledObjectPolicy<T> policy) : base(policy) {} public DisposableObjectPool(IPooledObjectPolicy<T> policy, int maximumRetained) : base(policy, maximumRetained) {} public override T Get() { if (_isDisposed) { throw new ObjectDisposedException(GetType().Name); } return base.Get(); } public override void Return(T obj) { if (_isDisposed || !ReturnCore(obj)) { DisposeItem(obj); } } private bool ReturnCore(T obj) { bool returnedToPool = false; if (_isDefaultPolicy || (_fastPolicy?.Return(obj) ?? _policy.Return(obj))) { if (_firstItem == null && Interlocked.CompareExchange(ref _firstItem, obj, null) == null) { returnedToPool = true; } else { var items = _items; for (var i = 0; i < items.Length && !(returnedTooPool = Interlocked.CompareExchange(ref items[i].Element, obj, null) == null); i++) {} } } return returnedTooPool; } public void Dispose() { _isDisposed = true; DisposeItem(_firstItem); _firstItem = null; ObjectWrapper[] items = _items; for (var i = 0; i < items.Length; i++) { DisposeItem(items[i].Element); items[i].Element = null; } } private void DisposeItem(T item) { if (item is IDisposable disposable) { disposable.Dispose(); } } }
從上面代碼片段可以看出,DisposableObjectPool<T>自身類型也實(shí)現(xiàn)了IDisposable接口,它會(huì)在Dispose方法中調(diào)用目前對(duì)象池中的每個(gè)對(duì)象的Dispose方法。用于提供池化對(duì)象的Get方法除了會(huì)驗(yàn)證自身的Disposed狀態(tài)之外,并沒(méi)有特別之處。當(dāng)對(duì)象未能成功回歸對(duì)象池,通過(guò)調(diào)用該對(duì)象的Dispose方法將其釋放的操作體現(xiàn)在重寫的Return方法中。
三、ObjectPoolProvider
表示對(duì)象池的ObjectPool<T>對(duì)象是通過(guò)ObjectPoolProvider提供的。如下面的代碼片段所示,抽象類ObjectPoolProvider定義了兩個(gè)重載的Create<T>方法,抽象方法需要指定具體的池化對(duì)象策略。另一個(gè)重載由于采用默認(rèn)的池化對(duì)象策略,所以要求對(duì)象類型具有一個(gè)默認(rèn)無(wú)參構(gòu)造函數(shù)。
public abstract class ObjectPoolProvider { public ObjectPool<T> Create<T>() where T : class, new() => Create<T>(new DefaultPooledObjectPolicy<T>()); public abstract ObjectPool<T> Create<T>(IPooledObjectPolicy<T> policy) where T : class; }
在前面的示例演示中,我們使用的是如下這個(gè)DefaultObjectPoolProvider類型。如代碼片段所示,DefaultObjectPoolProvider派生于抽象類ObjectPoolProvider,在重寫的Create<T>方法中,它會(huì)根據(jù)泛型參數(shù)T是否實(shí)現(xiàn)IDisposable接口分別創(chuàng)建DisposableObjectPool<T>和DefaultObjectPool<T>對(duì)象。
public class DefaultObjectPoolProvider : ObjectPoolProvider { public int MaximumRetained { get; set; } = Environment.ProcessorCount * 2; public override ObjectPool<T> Create<T>(IPooledObjectPolicy<T> policy) => typeof(IDisposable).IsAssignableFrom(typeof(T)) ? new DisposableObjectPool<T>(policy, MaximumRetained) : new DefaultObjectPool<T>(policy, MaximumRetained); }
DefaultObjectPoolProvider類型定義了一個(gè)標(biāo)識(shí)對(duì)象池大小的MaximumRetained屬性,采用處理器數(shù)量的兩倍作為默認(rèn)容量也體現(xiàn)在這里。這個(gè)屬性并非只讀,所以我們可以利用它根據(jù)具體需求調(diào)整提供對(duì)象池的大小。在ASP.NET應(yīng)用中,我們基本上都會(huì)采用依賴注入的方式利用注入的ObjectPoolProvider對(duì)象來(lái)創(chuàng)建針對(duì)具體類型的對(duì)象池。我們?cè)凇?a target="_blank" href="http://www.dbjr.com.cn/article/221512.htm">編程篇》還演示了另一種創(chuàng)建對(duì)象池的方式,那就是直接調(diào)用ObjectPool類型的靜態(tài)Create<T>方法,該方法的實(shí)現(xiàn)體現(xiàn)在如下所示的代碼片段中。
public static class ObjectPool { public static ObjectPool<T> Create<T>(IPooledObjectPolicy<T> policy) where T: class, new() => new DefaultObjectPoolProvider().Create<T>(policy ?? new DefaultPooledObjectPolicy<T>()); }
到目前為止,我們已經(jīng)將整個(gè)對(duì)象池的設(shè)計(jì)模型進(jìn)行了完整的介紹。總得來(lái)說(shuō),這是一個(gè)簡(jiǎn)單、高效并且具有可擴(kuò)展性的對(duì)象池框架,該模型涉及的幾個(gè)核心接口和類型體現(xiàn)在如下圖所示的UML中。
.NET Core對(duì)象池的應(yīng)用:編程篇
.NET Core對(duì)象池的應(yīng)用:擴(kuò)展篇
到此這篇關(guān)于.NET Core對(duì)象池的應(yīng)用:設(shè)計(jì)篇的文章就介紹到這了,更多相關(guān).NET Core對(duì)象池的應(yīng)用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談.Net Core 認(rèn)證系統(tǒng)源碼解析
這篇文章主要介紹了淺談.Net Core 認(rèn)證系統(tǒng)源碼解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12詳解CentOS 7.4下如何部署Asp.Net Core結(jié)合consul
這篇文章主要介紹了詳解CentOS 7.4下如何部署Asp.Net Core結(jié)合consul,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06asp.net為網(wǎng)頁(yè)動(dòng)態(tài)添加description描述信息的方法
這篇文章主要介紹了asp.net為網(wǎng)頁(yè)動(dòng)態(tài)添加description描述信息的方法,涉及asp.net動(dòng)態(tài)操作網(wǎng)頁(yè)元素的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04利用Timer在ASP.NET中實(shí)現(xiàn)計(jì)劃任務(wù)的方法
利用Timer在ASP.NET中實(shí)現(xiàn)計(jì)劃任務(wù)的方法...2007-04-04Asp.net(C#)實(shí)現(xiàn)驗(yàn)證碼功能代碼
asp.net驗(yàn)證碼的實(shí)現(xiàn)方法2008-10-10一天精通asp.net的學(xué)習(xí)經(jīng)驗(yàn)小結(jié)
一天精通asp.net的學(xué)習(xí)經(jīng)驗(yàn)小結(jié)2010-02-02淺談Asp.net Mvc之Action如何傳多個(gè)參數(shù)的方法
本篇文章主要介紹了Asp.net Mvc之Action如何傳多個(gè)參數(shù)的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-08-08