.NET Core中如何實現(xiàn)或使用對象池?
前言
池這個概念大家都很熟悉,比如我們經(jīng)常聽到數(shù)據(jù)庫連接池和線程池。它是一種基于使用預先分配資源集合的性能優(yōu)化思想。
簡單說,對象池就是對象的容器,旨在優(yōu)化資源的使用,通過在一個容器中池化對象,并根據(jù)需要重復使用這些池化對象來滿足性能上的需求。當一個對象被激活時,便被從池中取出。當對象被停用時,它又被放回池中,等待下一個請求。對象池一般用于對象的初始化過程代價較大或使用頻率較高的場景。
那在 .NET 中如何實現(xiàn)或使用對象池呢?
在 ASP.NET Core 框架里已經(jīng)內(nèi)置了一個對象池功能的實現(xiàn):Microsoft.Extensions.ObjectPool。如果是控制臺應(yīng)用程序,可以單獨安裝這個擴展庫。
池化策略
首先,要使用 ObjectPool,需要創(chuàng)建一個池化策略,告訴對象池你將如何創(chuàng)建對象,以及如何歸還對象。
該策略通過實現(xiàn)接口 IPooledObjectPolicy 來定義,下面是一個最簡單的策略實現(xiàn):
public class FooPooledObjectPolicy : IPooledObjectPolicy<Foo>
{
public Foo Create()
{
return new Foo();
}
public bool Return(Foo obj)
{
return true;
}
}
如果每次編碼都要定義這樣的策略,會比較麻煩,可以自己定義一個通用的泛型實現(xiàn)。Microsoft.Extensions.ObjectPool 中也提供了一個默認的泛型實現(xiàn):DefaultPooledObjectPolicy<T>。如果不需要定義復雜的構(gòu)造邏輯,使用默認的就行。下面我們來看看怎么使用。
對象池的使用
對象池使用的原則是:有借有還,再借不難。

當對象池中沒有實例時,則創(chuàng)建實例并返回給調(diào)用組件;當對象池中已有實例時,則直接取一個現(xiàn)有實例返回給調(diào)用組件。而且這個過程是線程安全的。
Microsoft.Extensions.ObjectPool 提供了默認的對象池實現(xiàn):DefaultObjectPool<T>,它提供了借 Get 和還 Return 操作接口。創(chuàng)建對象池時需要提供池化策略 IPooledObjectPolicy<T> 作為其構(gòu)造參數(shù)。
var policy = new DefaultPooledObjectPolicy<Foo>(); var pool = new DefaultObjectPool<Foo>(policy);
我們來看一個常規(guī)示例(C# 9.0 單文件完整代碼):
using Microsoft.Extensions.ObjectPool;
using System;
var policy = new DefaultPooledObjectPolicy<Foo>();
var pool = new DefaultObjectPool<Foo>(policy);
// 借
var item1 = pool.Get();
// 還
pool.Return(item1);
Console.WriteLine("item 1: {0}", item1.Id);
// 借
var item2 = pool.Get();
// 還
pool.Return(item2);
Console.WriteLine("item 2: {0}", item2.Id);
Console.ReadKey();
public class Foo
{
public string Id { get; set; } = Guid.NewGuid().ToString("N");
}
打印結(jié)果:

通過打印的 Id 知道,item1 和 item2 是同一樣對象。
我們再來看看只借不還會是什么樣子:
// ...
// 借
var item1 = pool.Get();
Console.WriteLine("item 1: {0}", item1.Id);
// 再借
var item2 = pool.Get();
Console.WriteLine("item 2: {0}", item2.Id);
// ...
打印結(jié)果:

可以看到,兩個對象是不同的實例。所以,當調(diào)用組件從對象池中借走一個對象實例,使用完后應(yīng)立即歸還給對象池,以便重復使用,避免因構(gòu)造新對象消耗過多資源。
指定對象池容量
在創(chuàng)建 DefaultObjectPool<T> 時,還可以指定第二個參數(shù):對象池的容量。它表示最大可從該對象池取出的對象數(shù)量,指定數(shù)量以外的被取走的對象將不會被池化。我來演示一下,大家就知道什么意思了,請看示例:
using Microsoft.Extensions.ObjectPool;
using System;
var policy = new DefaultPooledObjectPolicy<Foo>();
// 指定容量為 2。
var pool = new DefaultObjectPool<Foo>(policy, 2);
// 借走 3 個
var item1 = pool.Get();
Console.WriteLine("item 1: {0}", item1.Id);
var item2 = pool.Get();
Console.WriteLine("item 2: {0}", item2.Id);
var item3 = pool.Get();
Console.WriteLine("item 3: {0}", item3.Id);
// 再還會 3 個
pool.Return(item1);
pool.Return(item2);
pool.Return(item3);
// 再借走 3 個
var item4 = pool.Get();
Console.WriteLine("item 4: {0}", item4.Id);
var item5 = pool.Get();
Console.WriteLine("item 5: {0}", item5.Id);
var item6 = pool.Get();
Console.WriteLine("item 6: {0}", item6.Id);
Console.ReadKey();
注意示例代碼中我給對象池指定了容量為 2,然后借走 3 個再歸還 3 個,后面再借走 3 個。來看看打印結(jié)果:

我們看到,item1 與 item4 是同一個對象,item2 與 item5 是同一個對象。item3 與 item6 卻不是同一個對象。
也就是說,當對象從池中取出超過指定容量的對象數(shù)量,雖然歸還了相同數(shù)量的對象,但對象池只允許容納 2 個對象,第三個對象不會被池化。
在 ASP.NET Core 中使用
ASP.NET Core 框架內(nèi)置好了 Microsoft.Extensions.ObjectPool,不需要單獨安裝。官方文檔有個基于 ASP.NET Core 的使用示例:
https://docs.microsoft.com/en-us/aspnet/core/performance/objectpool
這個例子把 StringBuilder 做了池化。我這里就直接貼官方的例子了,為了更直觀些,我把無關(guān)的代碼簡化掉了。
先定義一個中間件:
public class BirthdayMiddleware
{
private readonly RequestDelegate _next;
public BirthdayMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context, ObjectPool<StringBuilder> builderPool)
{
var stringBuilder = builderPool.Get();
try
{
stringBuilder.Append("Hi");
// 其它處理
await context.Response.WriteAsync(stringBuilder.ToString());
}
finally // 即使出錯也要保證歸還對象
{
builderPool.Return(stringBuilder);
}
}
}
在 Startup 中注冊相應(yīng)的服務(wù)和中間件:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.TryAddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
services.TryAddSingleton<ObjectPool<StringBuilder>>(serviceProvider =>
{
var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
var policy = new StringBuilderPooledObjectPolicy();
return provider.Create(policy);
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMiddleware<BirthdayMiddleware>();
}
}
這個示例用了 DefaultObjectPoolProvider,它是默認的對象池 Provider,所以你也可以自定義自己的對象池 Provider。
總結(jié)
Microsoft.Extensions.ObjectPool 提供的對象池功能還是挺靈活的。普通場景使用使用默認的池化策略、默認的對象池和默認的對象池提供者就可以滿足需求,也可以自定義其中任意某部件來實現(xiàn)比較特殊或復雜的需求。
對象池的使用原則是:有借有還,再借不難。當調(diào)用組件從對象池中借走一個對象實例,使用完后應(yīng)立即歸還給對象池,以便重復利用,避免因過多的對象初始化影響系統(tǒng)性能。
到此這篇關(guān)于.NET Core中如何實現(xiàn)或使用對象池的文章就介紹到這了,更多相關(guān).NET Core使用對象池內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
asp.net html控件的File控件實現(xiàn)多文件上傳實例分享
asp.net中html控件的File控件實現(xiàn)多文件上傳簡單實例,開發(fā)工具vs2010使用c#語言,感興趣的朋友可以了解下,必定是多文件上傳值得學習,或許本文所提供的知識點對你有所幫助2013-02-02
在asp.NET 中使用SMTP發(fā)送郵件的實現(xiàn)代碼
本文簡單介紹了SMTP協(xié)議(RFC2554)發(fā)送郵件的過程,并討論了在 .NET 中使用SMTP發(fā)送郵件由簡到繁的三種不同方案、各自可能遇到的問題及其解決辦法2011-05-05
Excel、記事本數(shù)據(jù)導入到數(shù)據(jù)庫的實現(xiàn)方法
將手機號批量導入數(shù)據(jù)庫。思路:先將要導入的文件傳上項目里,然后讀取文件的每行數(shù)據(jù)并插入數(shù)據(jù)庫,操作完后再將上傳的文件刪除2013-10-10
.NET?Core使用?CancellationToken?取消API請求的操作方法
用戶取消請求時,你可以使用HttpContext.RequestAborted訪問,您也可以使用依賴注入將其自動注入到您的操作中,這篇文章主要介紹了.NET?Core使用?CancellationToken?取消API請求,需要的朋友可以參考下2024-03-03
如何使用Microsoft.Extensions.AI簡化.NET中的AI集成
Microsoft.Extensions.AI是一個創(chuàng)新的?.NET?庫,它為平臺開發(fā)人員提供了一個內(nèi)聚的?C#?抽象層,簡化了與大型語言模型(LLMs)和嵌入等AI服務(wù)的交互,本文給大家介紹如何使用Microsoft.Extensions.AI簡化.NET中的AI集成,感興趣的朋友一起看看吧2024-11-11
基于.Net?Core認證授權(quán)方案之JwtBearer認證
這篇文章介紹了基于.Net?Core認證授權(quán)方案之JwtBearer認證,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-06-06

