【C#/.NET】使用ASP.NET Core物件池

2023-05-28 18:01:04

Nuget

Microsoft.Extensions.ObjectPool

 

使用物件池的好處

減少初始化/資源分配,提高效能。這一條與執行緒池同理,有些物件的初始化或資源分配耗時長,複用這些物件減少初始化和資源分配。比如:我有一個執行耗時約500毫秒,記憶體空間 2KB的任務為此建立一個新執行緒非同步執行,而建立執行緒耗時1秒,記憶體空間佔用1MB則得不償失。

 

使用步驟

安裝Nuget包:Install-Package Microsoft.Extensions.ObjectPool

builder.Services.TryAddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();

使用物件池的第一步是實現IPooledObjectPolicy介面,要告訴物件池如何建立需要複用的物件

IPooledObjectPolicy介面有兩個方法,

T Create()負責建立複用物件。

Return負責將複用的物件釋放回物件池中。如果不呼叫Return,表示該物件在物件池被移除。

//物件池框架介面
public interface IPooledObjectPolicy<T> where T : notnull
{
    T Create();
    bool Return(T obj);
}

//我的複用物件的介面實現
public class ReuseObjectPolicy : IPooledObjectPolicy<ReuseObject>
{
    public ReuseObject Create()
    => new(DateTime.Now);

    public bool Return(ReuseObject obj)
    => true;
}
builder.Services.TryAddSingleton(serviceProvider =>
{
    var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
    var policy = new ReuseObjectPolicy();
    return provider.Create(policy);
});

物件使用通過依賴注入獲取泛型ObjectPool物件的Get使用,關於泛型ObjectPool的定義如下

T Get()負責獲取複用物件。

Return負責將複用的物件釋放回物件池中。如果不呼叫Return,表示該物件在物件池被移除。

public abstract class ObjectPool<T> where T : class
{
    public abstract T Get();
    public abstract void Return(T obj);
}

 

獲取ReuseObject複用物件,通過列印的建立事件和計數器可以知道,物件被複用了。而如果不呼叫Return,則會重新建立新的物件。

public class ReuseObject 
{
    private static  int _counter = 0;
    public ReuseObject(DateTime time)
    {
        Time = time;
        Interlocked.Increment(ref _counter);

        Console.WriteLine($"{Time}被建立了{_counter}次");
    }

    public DateTime Time { get; set; }
}

public class ObjectPoolController : ControllerBase
{
    private readonly ReuseObject _reuseObject;

    public ObjectPoolController(ObjectPool<ReuseObject> objectPool)
    {
        _reuseObject = objectPool.Get();
    }

    [HttpGet]
    public IActionResult Get()
    {
        var reuseObject = _objectPool.Get();
        try
        {
            Console.WriteLine($"建立時間是:{reuseObject.Time}");
        }
        finally
        {
            _objectPool.Return(reuseObject);
        }
        return Ok();
    }
}