Files
KinginfoGateway/src/Admin/ThingsGateway.NewLife.X/Collections/ObjectPool.cs
2248356998 qq.com 0b829ac85c 10.11.33
2025-09-08 21:16:37 +08:00

408 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections.Concurrent;
using ThingsGateway.NewLife.Log;
using ThingsGateway.NewLife.Reflection;
using ThingsGateway.NewLife.Threading;
namespace ThingsGateway.NewLife.Collections;
/// <summary>资源池。支持空闲释放,主要用于数据库连接池和网络连接池</summary>
/// <remarks>
/// 文档 https://newlifex.com/core/object_pool
/// </remarks>
/// <typeparam name="T"></typeparam>
public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull
{
#region
/// <summary>名称</summary>
public String Name { get; set; }
private Int32 _FreeCount;
/// <summary>空闲个数</summary>
public Int32 FreeCount => _FreeCount;
private Int32 _BusyCount;
/// <summary>繁忙个数</summary>
public Int32 BusyCount => _BusyCount;
/// <summary>最大个数。默认00表示无上限</summary>
public Int32 Max { get; set; } = 0;
/// <summary>最小个数。默认1</summary>
public Int32 Min { get; set; } = 1;
/// <summary>空闲清理时间。最小个数之上的资源超过空闲时间时被清理默认10s</summary>
public Int32 IdleTime { get; set; } = 10;
/// <summary>完全空闲清理时间。最小个数之下的资源超过空闲时间时被清理默认0s永不清理</summary>
public Int32 AllIdleTime { get; set; } = 0;
/// <summary>基础空闲集合。只保存最小个数,最热部分</summary>
private readonly ConcurrentStack<Item> _free = new();
/// <summary>扩展空闲集合。保存最小个数以外部分</summary>
private readonly ConcurrentQueue<Item> _free2 = new();
/// <summary>借出去的放在这</summary>
private readonly ConcurrentDictionary<T, Item> _busy = new();
//private readonly Object SyncRoot = new();
#endregion
#region
/// <summary>实例化一个资源池</summary>
public ObjectPool()
{
var str = GetType().Name;
if (str.Contains('`')) str = str.Substring(null, "`");
if (str != "Pool")
Name = str;
else
Name = $"Pool<{typeof(T).Name}>";
// 启动定期清理的定时器
StartTimer();
}
~ObjectPool()
{
this.TryDispose();
}
/// <summary>销毁</summary>
/// <param name="disposing"></param>
protected override void Dispose(Boolean disposing)
{
base.Dispose(disposing);
_timer.TryDispose();
WriteLog($"Dispose {typeof(T).FullName} FreeCount={FreeCount:n0} BusyCount={BusyCount:n0}");
Clear();
}
private volatile Boolean _inited;
private void Init()
{
if (_inited) return;
lock (lockThis)
{
if (_inited) return;
_inited = true;
WriteLog($"Init {typeof(T).FullName} Min={Min} Max={Max} IdleTime={IdleTime}s AllIdleTime={AllIdleTime}s");
}
}
#endregion
#region
private sealed class Item
{
/// <summary>数值</summary>
public T? Value { get; set; }
/// <summary>过期时间</summary>
public DateTime LastTime { get; set; }
}
#endregion
#region
/// <summary>借出</summary>
/// <returns></returns>
public virtual T Get()
{
Item? pi = null;
do
{
// 从空闲集合借一个
if (_free.TryPop(out pi) || _free2.TryDequeue(out pi))
{
Interlocked.Decrement(ref _FreeCount);
}
else
{
// 超出最大值后,抛出异常
var count = BusyCount;
if (Max > 0 && count >= Max)
{
var msg = $"申请失败,已有 {count:n0} 达到或超过最大值 {Max:n0}";
WriteLog("Acquire Max " + msg);
throw new Exception(Name + " " + msg);
}
// 借不到,增加
pi = new Item
{
Value = OnCreate(),
};
if (count == 0) Init();
#if DEBUG
WriteLog("Acquire Create Free={0} Busy={1}", FreeCount, count + 1);
#endif
}
// 借出时如果不可用,再次借取
} while (pi.Value == null || !OnGet(pi.Value));
// 最后时间
pi.LastTime = TimerX.Now;
// 加入繁忙集合
_busy.TryAdd(pi.Value, pi);
Interlocked.Increment(ref _BusyCount);
return pi.Value;
}
/// <summary>借出时是否可用</summary>
/// <param name="value"></param>
/// <returns></returns>
protected virtual Boolean OnGet(T value) => true;
/// <summary>申请资源包装项Dispose时自动归还到池中</summary>
/// <returns></returns>
public PoolItem<T> GetItem() => new(this, Get());
/// <summary>归还</summary>
/// <param name="value"></param>
public virtual Boolean Return(T value)
{
if (value == null) return false;
// 从繁忙队列找到并移除缓存项
if (!_busy.TryRemove(value, out var pi))
{
#if DEBUG
WriteLog("Return Error");
#endif
return false;
}
Interlocked.Decrement(ref _BusyCount);
// 是否可用
if (!OnReturn(value))
{
return false;
}
if (value is DisposeBase db && db.Disposed)
{
return false;
}
var min = Min;
// 如果空闲数不足最小值,则返回到基础空闲集合
if (_FreeCount < min /*|| _free.Count < min*/)
_free.Push(pi);
else
_free2.Enqueue(pi);
// 最后时间
pi.LastTime = TimerX.Now;
Interlocked.Increment(ref _FreeCount);
return true;
}
/// <summary>归还时是否可用</summary>
/// <param name="value"></param>
/// <returns></returns>
protected virtual Boolean OnReturn(T value) => true;
/// <summary>清空已有对象</summary>
public virtual Int32 Clear()
{
var count = _FreeCount + _BusyCount;
//_busy.Clear();
//_BusyCount = 0;
//_free.Clear();
//while (_free2.TryDequeue(out var rs)) ;
//_FreeCount = 0;
while (_free.TryPop(out var pi)) OnDispose(pi.Value);
while (_free2.TryDequeue(out var pi)) OnDispose(pi.Value);
_FreeCount = 0;
foreach (var item in _busy)
{
OnDispose(item.Key);
}
_busy.Clear();
_BusyCount = 0;
return count;
}
/// <summary>销毁</summary>
/// <param name="value"></param>
protected virtual void OnDispose(T? value) => value.TryDispose();
#endregion
#region
/// <summary>创建实例</summary>
/// <returns></returns>
protected virtual T? OnCreate() => (T?)typeof(T).CreateInstance();
#endregion
protected object lockThis = new();
#region
private TimerX? _timer;
private void StartTimer()
{
if (_timer != null) return;
lock (lockThis)
{
if (_timer != null) return;
_timer = new TimerX(Work, null, 5000, 5000) { Async = true };
}
}
private void Work(Object? state)
{
//// 总数小于等于最小个数时不处理
//if (FreeCount + BusyCount <= Min) return;
// 遍历并干掉过期项
var count = 0;
// 清理过期不还。避免有借没还
if (!_busy.IsEmpty)
{
var exp = TimerX.Now.AddSeconds(-AllIdleTime);
foreach (var item in _busy)
{
if (item.Value.LastTime < exp)
{
if (_busy.TryRemove(item.Key, out _))
{
// 业务层可能故意有借没还
//v.TryDispose();
Interlocked.Decrement(ref _BusyCount);
}
}
}
}
// 总数小于等于最小个数时不处理
if (IdleTime > 0 && !_free2.IsEmpty && FreeCount + BusyCount > Min)
{
var exp = TimerX.Now.AddSeconds(-IdleTime);
// 移除扩展空闲集合里面的超时项
while (_free2.TryPeek(out var pi) && pi.LastTime < exp)
{
// 取出来销毁。在并行操作中,此时返回可能是另一个对象
if (_free2.TryDequeue(out var pi2))
{
if (pi2.LastTime < exp)
{
pi2.Value.TryDispose();
count++;
Interlocked.Decrement(ref _FreeCount);
}
else
{
// 可能是另一个对象,放回去
_free2.Enqueue(pi2);
}
}
}
}
if (AllIdleTime > 0 && !_free.IsEmpty)
{
var exp = TimerX.Now.AddSeconds(-AllIdleTime);
// 移除基础空闲集合里面的超时项
while (_free.TryPeek(out var pi) && pi.LastTime < exp)
{
// 取出来销毁
if (_free.TryPop(out var pi2))
{
if (pi2.LastTime < exp)
{
pi2.Value.TryDispose();
count++;
Interlocked.Decrement(ref _FreeCount);
}
else
{
// 可能是另一个对象,放回去
_free.Push(pi2);
}
}
}
}
if (count > 0)
{
WriteLog("Release New={6:n0} Release={7:n0} Free={0} Busy={1} 清除过期资源 {2:n0} 项。", FreeCount, BusyCount, count);
}
}
#endregion
#region
/// <summary>日志</summary>
public ILog Log { get; set; } = Logger.Null;
/// <summary>写日志</summary>
/// <param name="format"></param>
/// <param name="args"></param>
public void WriteLog(String format, params Object?[] args)
{
if (Log?.Enable != true) return;
Log.Info(Name + "." + format, args);
}
#endregion
}
/// <summary>资源池包装项,自动归还资源到池中</summary>
/// <typeparam name="T"></typeparam>
public class PoolItem<T> : DisposeBase
{
#region
/// <summary>数值</summary>
public T Value { get; }
/// <summary>池</summary>
public IPool<T> Pool { get; }
#endregion
#region
/// <summary>包装项</summary>
/// <param name="pool"></param>
/// <param name="value"></param>
public PoolItem(IPool<T> pool, T value)
{
Pool = pool;
Value = value;
}
/// <summary>销毁</summary>
/// <param name="disposing"></param>
protected override void Dispose(Boolean disposing)
{
base.Dispose(disposing);
Pool.Return(Value);
}
#endregion
}