解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
大石头 authored at 2018-05-15 21:21:05
7.31 KiB
X
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;

namespace NewLife.Collections;

/// <summary>轻量级对象池。数组无锁实现,高性能</summary>
/// <remarks>
/// 文档 https://newlifex.com/core/object_pool
///
/// 设计说明:
/// - 存储结构采用 1 + N:热点槽位 <see cref="_current"/> 放置最热对象,内部数组存其余对象;
/// - 使用 <see cref="Interlocked.CompareExchange{T}(ref T, T, T)"/> 对热点槽位与数组元素进行无锁并发抢占/归还;
/// - 数组查找 O(N),且为结构体持有引用,避免对象级别的额外分配;
/// - 通过延迟初始化(双检 + 轻量锁)避免冷启动分配;
/// - 可选择在二代 GC 触发时定期清理,降低长时间闲置的内存占用(限速 60 秒一次)。
///
/// 线程安全说明:
/// - <see cref="Get"/> 与 <see cref="Return(T)"/> 对热点槽位与数组元素的读写使用 CAS,无需锁;
/// - <see cref="Init"/> 仅在首次分配时进入锁,后续不再加锁;
/// - <see cref="Clear"/> 与 <see cref="Get"/> / <see cref="Return(T)"/> 可并发执行,依靠局部快照与空值判定避免空引用;
/// - 初始化后修改 <see cref="Max"/> 不会影响已分配的内部数组容量,此为设计约束。
///
/// 兼容性:支持 .NET Framework 4.5 起与 .NET Standard / .NET Core / .NET 5+ 多目标。
/// </remarks>
/// <typeparam name="T">池化的引用类型</typeparam>
public class Pool<T> : IPool<T> where T : class
{
    #region 属性
    /// <summary>对象池大小。默认 CPU*2,初始化后改变无效</summary>
    public Int32 Max { get; set; }

    private Item[]? _items;
    private T? _current;

    // 专用同步对象。避免在 Init 阶段锁定 this,降低被外部误锁的风险
    private readonly Object _sync = new();

    struct Item
    {
        public T? Value;
    }
    #endregion

    #region 构造
    /// <summary>实例化对象池。默认大小 CPU*2,且最小为 8</summary>
    /// <param name="max">最大对象数。小于等于 0 使用 CPU*2;小于 8 则固定为 8</param>
    public Pool(Int32 max = 0)
    {
        if (max <= 0) max = Environment.ProcessorCount * 2;
        if (max < 8) max = 8;

        Max = max;
    }

    /// <summary>实例化对象池,并在 GC 第二代触发时尝试清理</summary>
    /// <param name="max">最大对象数。默认大小 CPU*2</param>
    /// <param name="useGcClear">是否在二代 GC 触发时清理池里对象(限速 60 秒一次)</param>
    protected Pool(Int32 max, Boolean useGcClear) : this(max)
    {
        if (useGcClear) Gen2GcCallback.Register(s => (s as Pool<T>)!.OnGen2(), this);
    }

    private Int64 _next;
    private Boolean OnGen2()
    {
        var now = Runtime.TickCount64;
        if (_next <= 0)
            _next = now + 60000;
        else if (_next < now)
        {
            Clear();
            _next = now + 60000;
        }

        return true;
    }

    [MemberNotNull(nameof(_items))]
    private Item[] Init()
    {
        // 双检锁:仅首次分配进入临界区。
        if (_items == null)
        {
            lock (_sync)
            {
                _items ??= new Item[Max - 1];
            }
        }

        return _items;
    }
    #endregion

    #region 方法
    /// <summary>获取一个实例;若池中为空则创建</summary>
    /// <returns>对象实例</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public virtual T Get()
    {
        // 先尝试获取热点槽位,最快路径
        var val = _current;
        if (val != null && Interlocked.CompareExchange(ref _current, null, val) == val) return val;

        // 扫描内部数组
        var items = Init();
        for (var i = 0; i < items.Length; i++)
        {
            val = items[i].Value;
            if (val != null && Interlocked.CompareExchange(ref items[i].Value, null, val) == val) return val;
        }

        // 未命中,按策略创建新实例
        var rs = OnCreate();
        if (rs == null) throw new InvalidOperationException($"[Pool] Unable to create an instance of [{typeof(T).FullName}]");

        return rs;
    }

    /// <summary>异步获取一个实例;轻量池无 IO,直接同步包装</summary>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public virtual Task<T> GetAsync(CancellationToken cancellationToken = default) => Task.FromResult(Get());

    /// <summary>归还</summary>
    /// <param name="value">归还的对象实例</param>
    /// <returns>是否成功放入池中;池满返回 false</returns>
    [Obsolete("Please use Return from 2024-02-01")]
    public virtual Boolean Put(T value) => Return(value);

    /// <summary>归还</summary>
    /// <param name="value">归还的对象实例</param>
    /// <returns>是否成功放入池中;池满返回 false</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public virtual Boolean Return(T value)
    {
        // 先尝试放入热点槽位
        if (_current == null && Interlocked.CompareExchange(ref _current, value, null) == null) return true;

        // 再尝试放入内部数组
        var items = Init();
        for (var i = 0; i < items.Length; ++i)
        {
            if (Interlocked.CompareExchange(ref items[i].Value, value, null) == null) return true;
        }

        // 池已满
        return false;
    }

    /// <summary>清空对象池,返回被清理的对象数量</summary>
    /// <returns>清理的数量</returns>
    [UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "运行时类型反射/动态加载边界;AOT 下不受支持或需改用泛型入口(见分析稿 §4/§5)")]
    public virtual Int32 Clear()
    {
        var count = 0;

        // 清理热点槽位
        if (_current != null)
        {
            _current = null;
            count++;
        }

        // 快照内部数组,避免并发期间替换导致的空引用
        var items = _items;
        if (items == null) return count;

        for (var i = 0; i < items.Length; ++i)
        {
            if (items[i].Value != null)
            {
                items[i].Value = null;
                count++;
            }
        }
        _items = null;

        return count;
    }
    #endregion

    #region 重载
    /// <summary>创建实例。默认使用 AOT 安全的泛型工厂创建,可在子类中重写以自定义创建策略。</summary>
    /// <remarks>
    /// 要求 T 具备公开无参构造。原实现 typeof(T).CreateInstance() 依赖运行时反射构造:
    /// NativeAOT 下被池化类型的反射构造器会被裁剪,运行期抛 MissingMethodException
    /// (网络接收链路 ReceivedEventArgs 每包必炸、网络栈瘫痪的根因),故改用泛型工厂。
    /// </remarks>
    /// <returns>新建对象实例;允许返回 null(将抛出异常)</returns>
    [UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "无工厂委托时使用无参构造创建对象;AOT 下裁剪可能移除无参构造,应传入工厂委托")]
    protected virtual T? OnCreate() => Activator.CreateInstance<T>();
    #endregion
}