設計一個.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);
}
}
}
View Code
初始化一個對象池
最大活動對象數量 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。