設計一個.net對象池
對象池對於創建開銷比較大的對象來說很有意義,為了優化程序的運行速度、避免頻繁創建銷毀開銷比較大的對象,我們可以通過對象池來復用創建開銷大的對象。對象池的思路比較簡單,事先創建好一批對象,放到一個集合中,以后每當程序需要新的對象時候,都從對象池里獲取,每當程序用完該對象后,都把該對象歸還給對象池。這樣會避免重復的對象創建,提高程序性能。
應用場景
在Anno微服務框架中的使用,由於客戶端調用微服的時候需要建立Socket連接,頻繁的創建和銷毀連接會有很大的開銷。所以我們設想我們如果可以重復利用這些對象那么性能上也會是很大的提升。這時候我們就需要一個對象池來存放這些可以重復利用的對象。不僅如此我們還需要可以控制對象池的最大存活對象數量、最小閑置對象數量、最大閑置對象數量、如何創建對象、銷毀對象、定期清理閑置對象。(網上沒找到好用的於是開始造我們的輪子)
Install-Package Anno.XObjectPool -Version 1.0.3.4
Xpool核心代碼

using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Anno.XObjectPool { public class XPool<T> : IDisposable { private bool disposed; private XPoolConfiguration xcfg; /// <summary> /// 初始化對象池 /// </summary> /// <param name="createObject">創建XObject對象</param> /// <param name="activeXObject"> 獲取XObject對象之前驗證True 有效</param> public XPool(Func<T> createObject, Func<T, bool> activeXObject = null) : this(new XPoolConfiguration() { MaxActive = 1000, MaxIdle = 400, MinIdle = 10 }, createObject, activeXObject) { } /// <summary> /// 初始化對象池 /// </summary> /// <param name="maxActive">最大活動數量</param> /// <param name="minIdle">最小空閑數量</param> /// <param name="maxIdle">最大空閑數量</param> /// <param name="createObject">創建XObject對象</param> /// <param name="activeXObject"> 獲取XObject對象之前驗證True 有效</param> public XPool(int maxActive, int minIdle, int maxIdle, Func<T> createObject, Func<T, bool> activeXObject = null) { xcfg = new XPoolConfiguration() { MaxActive = maxActive, MaxIdle = maxIdle, MinIdle = minIdle }; pools = new ConcurrentStack<XObject<T>>(); ResetEvent = new AutoResetEvent(false); if (createObject != null) { CreateXObject = createObject; } else { throw new ArgumentNullException("createObject 不能為空"); } if (activeXObject != null) { ActiveXObject = activeXObject; } Parallel.For(0, minIdle, x => { pools.Push(new XObject<T>() { Value = CreateXObject.Invoke(), LastDateTime = DateTime.Now, Pool = this }); }); StartTaskClearLongIdleXObject(); } /// <summary> /// 初始化對象池 /// </summary> /// <param name="xcfg">對象池配置</param> /// <param name="createObject">創建XObject對象</param> /// <param name="activeXObject"> 獲取XObject對象之前驗證True 有效</param> public XPool(XPoolConfiguration xcfg, Func<T> createObject, Func<T, bool> activeXObject = null) : this(xcfg.MaxActive, xcfg.MinIdle, xcfg.MaxIdle, createObject, activeXObject) { } private ConcurrentStack<XObject<T>> pools; private int _activedTransportCount = 0; private AutoResetEvent ResetEvent { get; set; } /// <summary> /// 活動鏈接數量 /// </summary> public int ActivedTransportCount => _activedTransportCount; /// <summary> /// 原子性增加 活動鏈接數量 /// </summary> private void InterlockedIncrement() { Interlocked.Increment(ref _activedTransportCount); } /// <summary> /// 原子性減少 活動鏈接數量 /// </summary> private void InterlockedDecrement() { Interlocked.Decrement(ref _activedTransportCount); } public XObject<T> Borrow(TimeSpan? timeout = null) { if (!pools.TryPop(out XObject<T> xobj)) { if (pools.Count < xcfg.MinIdle && _activedTransportCount < xcfg.MaxActive) { pools.Push(new XObject<T>() { Value = CreateXObject.Invoke(), LastDateTime = DateTime.Now, Pool = this }); } if (!pools.Any() && _activedTransportCount >= xcfg.MaxActive) { int millisecondsTimeout = 20000; if (timeout.HasValue && timeout.Value.TotalMilliseconds > 0) { millisecondsTimeout = (int)timeout.Value.TotalMilliseconds; } bool result = ResetEvent.WaitOne(millisecondsTimeout); if (!result) { throw new TimeoutException($"Timeout對象池等待超時!"); } } if (!pools.TryPop(out xobj)) { xobj = new XObject<T>() { Value = CreateXObject.Invoke(), LastDateTime = DateTime.Now, Pool = this }; } } InterlockedIncrement(); //借出之前判斷對象是否有效 if (!ActiveXObject(xobj.Value)) { throw new InvalidOperationException("對象無效,請在有效性檢測函數activeXObject中設置有效性"); } return xobj; } public void Return(XObject<T> xObject, bool isDispose = false) { if (xObject == null) { throw new ArgumentNullException("xObject 不能為空!"); } /* * 主動釋放的釋放 * 超出最大閑置數量的釋放 * 無效的釋放 */ if (isDispose || _activedTransportCount > xcfg.MaxIdle || !ActiveXObject(xObject.Value)) { DisposeXObject(xObject); xObject.Pool = null; InterlockedDecrement(); return; } xObject.LastDateTime = DateTime.Now; pools.Push(xObject); InterlockedDecrement(); ResetEvent.Set(); } private void Dispose(bool disposing) { if (!disposed) { if (disposing) { try { while (pools.TryPop(out XObject<T> xobj)) { //Pool 釋放的時候XObject不再歸還到Pool DisposeXObject(xobj); xobj.Pool = null; } } catch (Exception) { } } disposed = true; } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// <summary> /// 創建XObject對象 /// </summary> public Func<T> CreateXObject { get; set; } = () => { return default(T); }; /// <summary> /// 獲取XObject對象之前驗證True 有效 /// </summary> public Func<T, bool> ActiveXObject { get; set; } = x => { return true; }; /// <summary> /// 釋放XObject時候觸發 /// </summary> public Action<XObject<T>> DisposeXObject { get; set; } = x => { }; /// <summary> /// 移除長度為count的元素 /// </summary> /// <param name="count">除元素的長度count</param> private void DisposeLongIdleXObject(int count) { int startIndex = pools.Count - count; XObject<T>[] popXObjects = new XObject<T>[count]; pools.TryPopRange(popXObjects, 0, count); for (int i = 0; i < popXObjects.Length; i++) { Return(popXObjects[i], true); } } /// <summary> /// 每隔10秒檢測一次清理30秒未使用的對象數量的對象 /// (如果存在對象30未使用,說明對象池有對象長時間閑置未使用)則從頭部彈出一定數量的對象釋放掉 /// </summary> private void StartTaskClearLongIdleXObject() { Task.Factory.StartNew(async () => { while (!disposed) { await Task.Delay(10000); try { var removeCount = 0; var now = DateTime.Now.AddSeconds(-30); var _pools = pools.ToList(); for (int i = _pools.Count - 1; i >= xcfg.MinIdle; i--) { if (_pools[i].LastDateTime < now) { removeCount++; } } if (removeCount > 0 && removeCount <= (pools.Count - xcfg.MinIdle)) { DisposeLongIdleXObject(removeCount); } } finally { } } }, TaskCreationOptions.LongRunning); } } }
初始化一個對象池
最大活動對象數量 50個,最小閑置對象數量2個,最大閑置數量20個。
var UserPool = new XPool<User>(50, 2, 20, () => { int age = Interlocked.Increment(ref _activedTransportCount); return new User() { Age = age, Name = $"Name{age}" }; });
並行調用
200個並行調用
Parallel.For(0, 200, x => { using (var user = UserPool.Borrow()) { Console.WriteLine($"Age:{user.Value.Age},Name:{user.Value.Name}");//,Msg:{user.Value.Msg} } });
結果:
從上圖我們看到在200個並行過程中,只有4個對象被使用。因此可以看出我們沒有頻繁的創建對象。
歡迎加入QQ群:478399354 ,到這里我們互為師長項目學習。
Anno開源地址:
AnnoGitHub源碼:https://github.com/duyanming/Anno.Core
AnnoGitee源碼:https://gitee.com/dotnetchina/anno.core
Viper示例項目:https://github.com/duyanming/Viper
體驗地址:http://140.143.207.244/Home/Login
文檔地址:https://duyanming.github.io/
關於Anno的更多內容,隨后更新。敬請關注。開源不易,感謝Star。