.NET?Core?中對象池?Object?Pool的使用
一、什么是對象池
對象池簡單來說就是一種為對象提供可復用能力的軟件設計思路。我們常說有借有還,再借不難,而對象池就是通過借和還這樣兩個動作來保證對象可以被重復使用,從而節(jié)省頻繁創(chuàng)建對象的性能開銷。對象池最常用的場景是游戲設計,因為在游戲中大量存在著可復用的對象,源源不斷的子彈出現(xiàn)并不是循環(huán)再生的。在數(shù)據(jù)庫中存在著被稱為連接池的東西,每當出現(xiàn)數(shù)據(jù)庫無法連接的情況時,經(jīng)驗豐富的開發(fā)人員往往會先檢查連接池是否滿了,這其實就是對象池模式在特定領域的具體實現(xiàn)。因此對象池本質上就是負責一組對象創(chuàng)建和銷毀的容器。 對象池最大的優(yōu)勢是可以自主地管理池子內的每個對象,決定它們是需要被回收還是可以重復使用。我們都知道創(chuàng)建一個新對象需要消耗一定的系統(tǒng)資源,一旦這些對象可以重復地使用就可以節(jié)省系統(tǒng)資源開銷,這對提高系統(tǒng)性能會非常有幫助。下面的代碼實微軟官方文檔實現(xiàn)的一個簡單的對象池:
public class ObjectPool<T> : IObjectPool<T>
{
private Func<T> _instanceFactory;
private ConcurrentBag<T> _instanceItems;
public ObjectPool(Func<T> instanceFactory)
{
_instanceFactory = instanceFactory ??
throw new ArgumentNullException(nameof(instanceFactory));
_instanceItems = new ConcurrentBag<T>();
}
public T Get()
{
T item;
if (_instanceItems.TryTake(out item)) return item;
return _instanceFactory();
}
public void Return(T item)
{
_instanceItems.Add(item);
}
}
二、.NET Core 中的對象池
在.NET Core 中微軟已經(jīng)為我們提供了對象池的實現(xiàn),即Microsoft.Extensions.ObjectPool。它主要提供了三個核心的組件,分別是ObjectPool、ObjectPoolProvider和IPooledObjectPolicy。ObjectPool是一個抽象類,對外提供了Get和Return兩個方法,這就是所謂的有借有還。ObjectPoolProvider同樣是一個抽象類,它的職責就是創(chuàng)建ObjectPool,它提供了兩個Create方法,兩者的區(qū)別是無參數(shù)版本本質上使用的是DefaultPooledObjectPolicy。它和DefaultObjectPool、DefaultObjectPoolProvider都是微軟提供的默認實現(xiàn),IPooledObjectPolicy可以為不同的對象池定義不同的策略,來決定對象如何借、是否可以還。DefaultObjectPool內部使用ObjectWrapper[]來管理對象,ObjectWrapper[]的大小等于 maximumRetained-1,默認情況下maximumRetained等于Environment.ProcessorCount * 2,這里主要用到了Interlocked.CompareExchange()方法,
具體代碼如下:
public override T Get()
{
var item = _firstItem;
if (item == null || Interlocked.CompareExchange(ref _firstItem, null, item) != item)
{
var items = _items;
for (var i = 0; i < items.Length; i++)
{
item = items[i].Element;
if (item != null && Interlocked.CompareExchange(ref items[i].Element, null, item) == item)
{
return item;
}
}
item = Create();
}
return item;
}
// Non-inline to improve its code quality as uncommon path
[MethodImpl(MethodImplOptions.NoInlining)]
private T Create() => _fastPolicy?.Create() ?? _policy.Create();
public override void Return(T obj)
{
if (_isDefaultPolicy || (_fastPolicy?.Return(obj) ?? _policy.Return(obj)))
{
if (_firstItem != null || Interlocked.CompareExchange(ref _firstItem, obj, null) != null)
{
var items = _items;
for (var i = 0; i < items.Length && Interlocked.CompareExchange(ref items[i].Element, obj, null) != null; ++i)
{
}
}
}
}
這里用到Interlocked.CompareExchange()方法,Get()方法將items[i].Element和null進行交換,將指定元素設為 null 并返回原始值。Return()方法將items[i].Element和obj交換后的值不為 null,表示指定元素已經(jīng)歸還,這個方法只有在第一個參數(shù)和第三個參數(shù)相等時才會發(fā)生交換。
說了這么多,我們來看一下對象池具體的用法:
var service = new ServiceCollection();
//使用DefaultObjectPoolProvider
service.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
//使用默認策略
service.AddSingleton<ObjectPool<Foo>>(serviceProvider =>
{
var objectPoolProvider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
return objectPoolProvider.Create<Foo>();
});
//使用自定義策略
service.AddSingleton<ObjectPool<Foo>>(serviceProvider =>
{
var objectPoolProvider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
return objectPoolProvider.Create(new FooObjectPoolPolicy());
});
var serviceProvider = _service.BuildServiceProvider();
var objectPool = _serviceProvider.GetService<ObjectPool<Foo>>();
//有借有還,兩次是同一個對象
var item1 = objectPool.Get();
objectPool.Return(item1);
var item2 = objectPool.Get();
Assert.AreEqual(item1, item2);//true
//有借無還,兩次是不同的對象
var item3 = objectPool.Get();
var item4 = objectPool.Get();
Assert.AreEqual(item3, item4);//false
上面的代碼中Foo和FooObjectPoolPolicy是兩個工具類:
public class Foo
{
public string Id { get; set; }
public DateTime? CreatedAt { get; set; }
public string CreatedBy { get; set; }
}
public class FooObjectPoolPolicy : IPooledObjectPolicy<Foo>
{
public Foo Create()
{
return new Foo()
{
Id = Guid.NewGuid().ToString("N"),
CreatedAt = DateTime.Now,
CreatedBy = "zs"
};
}
public bool Return(Foo obj)
{
return true;
}
}
TIP:當你需要控制對象池內的對象如何被創(chuàng)建的時候,你可以考慮實現(xiàn)自定義的IPooledObjectPolicy<T>,反之DefaultPooledObjectPolicy<T>實現(xiàn)完全可以滿足你的使用。
三、本文小結
實現(xiàn)對象池可以考慮ConcurrentBag、Stack、Queue以及BlockingCollection等多種數(shù)據(jù)結構,而微軟在.NET Core 中已經(jīng)為我們實現(xiàn)了一個簡單的對象池,大多數(shù)情況下,我們只需要定義自己的IPooledObjectPolicy去決定對象應該怎么樣借、怎么樣還。總之游戲世界里的 GameObject、數(shù)據(jù)庫里的連接池,都是對象池模式在各自領域中的具體實現(xiàn)。
TIP:對象池是一種通過復用對象來減少資源開銷進而實現(xiàn)提高系統(tǒng)性能的軟件設計模式,其核心是控制容器內對象的生命周期來規(guī)避系統(tǒng)的主動回收,從對象池中借出的對象必須要及時歸還,否則會造成對象池中沒有可用資源。
到此這篇關于 .NET Core 中對象池 Object Pool的使用的文章就介紹到這了,更多相關 .NET Core 中對象池 內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
ASP.NET Core WebSocket集群實現(xiàn)思路詳解
這篇文章主要為大家介紹了ASP.NET Core WebSocket集群實現(xiàn)思路詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11
ASP.NET MVC Web API HttpClient簡介
依稀還記得那個時候用WebClient,HttpWebRequest來發(fā)送一個請求,現(xiàn)在ASP.NET MVC4中自帶了一個類HttpClient;需要的朋友可以參考下2012-11-11

