最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C#.NET ObjectPool 對(duì)象復(fù)用、池化策略與使用邊界

 更新時(shí)間:2026年06月03日 09:23:14   作者:唐青楓  
本文主要介紹了C#.NET ObjectPool 對(duì)象復(fù)用、池化策略與使用邊界,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

簡介

.NET 里做性能優(yōu)化時(shí),很多人第一反應(yīng)是:

  • 少分配
  • GC
  • 少臨時(shí)對(duì)象

這個(gè)方向本身沒有問題。

但問題在于,優(yōu)化一旦開始,很容易走偏成另外一種極端:

  • 看到對(duì)象創(chuàng)建就想池化
  • 覺得池化一定比 new 更快
  • 把對(duì)象池當(dāng)成一種“萬能緩存”

ObjectPool 這類工具,真正值錢的地方不是“把所有對(duì)象都放進(jìn)池里”,而是:

對(duì)某些創(chuàng)建成本不算低、使用頻率高、生命周期短、又能安全復(fù)用的對(duì)象,減少重復(fù)分配和回收的成本。

這篇文章重點(diǎn)講清楚幾件事:

  • ObjectPool 到底是什么;
  • 為什么會(huì)有它;
  • .NET 里的官方實(shí)現(xiàn)怎么用;
  • 怎么安裝;
  • 一個(gè)最小 demo 怎么跑;
  • 自定義池化對(duì)象怎么寫;
  • 它適合什么場(chǎng)景,不適合什么場(chǎng)景。

一句話先給結(jié)論:

ObjectPool 不是為了替代正常對(duì)象創(chuàng)建,而是為了在特定熱點(diǎn)路徑里復(fù)用“可重置的短期對(duì)象”。

ObjectPool到底是什么?

它位于:

Microsoft.Extensions.ObjectPool

核心抽象很簡單:

ObjectPool<T>

最重要的兩個(gè)動(dòng)作也很簡單:

  • Get():從池里取一個(gè)對(duì)象
  • Return(obj):把對(duì)象還回池里

它的思路不是:

  • 預(yù)先把所有對(duì)象都準(zhǔn)備好
  • 永遠(yuǎn)保證對(duì)象不創(chuàng)建

而更像:

  • 能復(fù)用就復(fù)用
  • 復(fù)用不了就新建
  • 還回來時(shí)如果池里放不下,也可以直接丟掉

所以一開始就要建立一個(gè)正確心智模型:

對(duì)象池是“盡量復(fù)用”,不是“絕不分配”。

為什么會(huì)有它?

先看一個(gè)最典型的例子:

for (var i = 0; i < 100_000; i++)
{
    var sb = new StringBuilder();
    sb.Append("hello");
    _ = sb.ToString();
}

這段代碼邏輯上沒問題,但如果它出現(xiàn)在高頻路徑里,就會(huì)帶來很直接的成本:

  • 持續(xù)分配對(duì)象
  • 增加 GC 壓力
  • 熱點(diǎn)路徑上吞吐變差

如果這個(gè)對(duì)象滿足幾件事:

  • 創(chuàng)建本身有一定成本
  • 用完之后能清理干凈
  • 不會(huì)被跨線程亂共享
  • 高頻重復(fù)出現(xiàn)

那就有池化的價(jià)值。

所以 ObjectPool 解決的不是“對(duì)象太多”這么寬泛的問題,而是更具體的這一類問題:

  • 某些對(duì)象很適合復(fù)用
  • 反復(fù)創(chuàng)建和回收它們不劃算

它和緩存、連接池、數(shù)組池是什么關(guān)系?

這幾個(gè)概念經(jīng)常被混在一起,但其實(shí)不是一回事。

1. 和緩存不一樣

緩存關(guān)心的是:

  • 數(shù)據(jù)值能不能復(fù)用
  • 下次還能不能直接命中

對(duì)象池關(guān)心的是:

  • 這個(gè)對(duì)象實(shí)例能不能再利用

也就是說,緩存復(fù)用的是“結(jié)果”,對(duì)象池復(fù)用的是“殼”。

2. 和數(shù)據(jù)庫連接池不一樣

連接池里的資源通常更重,而且?guī)в型獠肯到y(tǒng)狀態(tài)。

對(duì)象池更常見的目標(biāo)通常是:

  • StringBuilder
  • 序列化緩沖對(duì)象
  • 臨時(shí) parser
  • 可重置的上下文對(duì)象

3. 和ArrayPool<T>不一樣

ArrayPool<T> 復(fù)用的是數(shù)組緩沖區(qū)。

ObjectPool<T> 復(fù)用的是一個(gè)完整對(duì)象。

兩者都屬于“減少分配”的工具,但粒度不同。

安裝

官方包是:

dotnet add package Microsoft.Extensions.ObjectPool

如果你習(xí)慣看 csproj,對(duì)應(yīng)就是:

<ItemGroup>
  <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="*" />
</ItemGroup>

常用命名空間:

using Microsoft.Extensions.ObjectPool;

怎么自己建一個(gè)最小 demo 跑起來?

先建一個(gè)控制臺(tái)項(xiàng)目:

dotnet new console -n ObjectPoolDemo
cd ObjectPoolDemo
dotnet add package Microsoft.Extensions.ObjectPool

然后把 Program.cs 改成下面這樣:

using Microsoft.Extensions.ObjectPool;
using System.Text;

var provider = new DefaultObjectPoolProvider();
var pool = provider.CreateStringBuilderPool();

var sb = pool.Get();

try
{
    sb.Append("Hello ");
    sb.Append("ObjectPool");

    Console.WriteLine(sb.ToString());
}
finally
{
    pool.Return(sb);
}

最后執(zhí)行:

dotnet run

如果正常輸出:

Hello ObjectPool

那就說明這條最小鏈路已經(jīng)通了。

這個(gè)最小 demo 里最重要的不是 API 名字,而是這個(gè)使用姿勢(shì):

  • Get()
  • 用完以后一定 Return()
  • 最穩(wěn)妥的寫法是放在 try/finally

ObjectPool的核心工作方式是什么?

不用先看源碼,先抓住主線就夠了。

你可以把它粗略理解成這樣:

Get:
  池里有空閑對(duì)象 -> 直接拿
  池里沒有       -> 新建一個(gè)

Return:
  對(duì)象可復(fù)用 + 池里還有空間 -> 放回去
  否則                      -> 直接丟棄

這意味著一個(gè)很容易被忽略的事實(shí):

池的大小限制的是“最多保留多少個(gè)可復(fù)用對(duì)象”,不是“程序一共最多只能創(chuàng)建多少個(gè)對(duì)象”。

如果并發(fā)一高,池子里不夠用,ObjectPool 仍然會(huì)創(chuàng)建新對(duì)象。

所以它不是限流器,也不是容量控制器。

DefaultObjectPoolProvider、ObjectPool<T>、PooledObjectPolicy<T>分別是什么?

這幾個(gè)類型建議一起理解。

1.ObjectPool<T>

這是抽象池本身。

它定義的就是:

  • Get()
  • Return()

2.DefaultObjectPoolProvider

這是默認(rèn)的池工廠。

你通常不會(huì)每次都直接 new 某個(gè)內(nèi)部池實(shí)現(xiàn),而是用它來創(chuàng)建池:

var provider = new DefaultObjectPoolProvider();
var pool = provider.CreateStringBuilderPool();

或者:

var provider = new DefaultObjectPoolProvider();
var pool = provider.Create(new MyBufferPolicy());

3.PooledObjectPolicy<T>

這個(gè)很關(guān)鍵。

它決定了兩件事:

  • 對(duì)象怎么創(chuàng)建
  • 對(duì)象歸還時(shí)能不能重新進(jìn)入池

也就是說,池化不僅是“有個(gè)容器”,還要有一套規(guī)則。

自定義對(duì)象池怎么寫?

如果你要池化的是自己的類型,通常會(huì)先寫一個(gè)策略類。

先準(zhǔn)備一個(gè)可復(fù)用對(duì)象:

public sealed class ReusableBuffer
{
    public byte[] Buffer { get; } = new byte[4096];
    public int Length { get; set; }

    public void Reset()
    {
        Length = 0;
        Array.Clear(Buffer, 0, Buffer.Length);
    }
}

然后寫一個(gè)策略:

using Microsoft.Extensions.ObjectPool;

public sealed class ReusableBufferPolicy : PooledObjectPolicy<ReusableBuffer>
{
    public override ReusableBuffer Create()
    {
        return new ReusableBuffer();
    }

    public override bool Return(ReusableBuffer obj)
    {
        obj.Reset();
        return true;
    }
}

最后創(chuàng)建并使用池:

var provider = new DefaultObjectPoolProvider();
var pool = provider.Create(new ReusableBufferPolicy());

var buffer = pool.Get();

try
{
    buffer.Buffer[0] = 100;
    buffer.Length = 1;
}
finally
{
    pool.Return(buffer);
}

這里最重要的是 Return() 里的邏輯。

因?yàn)閷?duì)象要不要重新進(jìn)入池,不是無條件的。

你完全可以在這里做判斷:

  • 對(duì)象狀態(tài)不對(duì),不回池
  • 對(duì)象太大,不回池
  • 對(duì)象被污染,不回池

例如:

public override bool Return(ReusableBuffer obj)
{
    if (obj.Buffer.Length > 1024 * 1024)
    {
        return false;
    }

    obj.Reset();
    return true;
}

這類寫法在工程上很有價(jià)值,因?yàn)橛行?duì)象一旦膨脹得太大,繼續(xù)留在池里反而會(huì)浪費(fèi)內(nèi)存。

IResettable是什么?

官方實(shí)現(xiàn)還提供了一個(gè)很實(shí)用的思路:

IResettable

如果對(duì)象本身就知道怎么把自己恢復(fù)到可復(fù)用狀態(tài),那它就更適合池化。

可以把它理解成:

  • PooledObjectPolicy<T> 負(fù)責(zé)池規(guī)則
  • IResettable 更像對(duì)象自己聲明“我知道怎么重置自己”

這類設(shè)計(jì)的好處是職責(zé)更清楚:

  • 池負(fù)責(zé)借還
  • 對(duì)象負(fù)責(zé)恢復(fù)

在 ASP.NET Core 里怎么接到 DI?

如果你不想手動(dòng)在每個(gè)地方 new DefaultObjectPoolProvider,更自然的方式通常是接進(jìn) DI

例如:

using Microsoft.Extensions.ObjectPool;

var builder = WebApplication.CreateBuilder(args);

builder.Services.TryAddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
builder.Services.TryAddSingleton<ObjectPool<StringBuilder>>(sp =>
{
    var provider = sp.GetRequiredService<ObjectPoolProvider>();
    return provider.CreateStringBuilderPool();
});

后面在服務(wù)里直接注入:

public sealed class MessageBuilderService
{
    private readonly ObjectPool<StringBuilder> _pool;

    public MessageBuilderService(ObjectPool<StringBuilder> pool)
    {
        _pool = pool;
    }

    public string Build(string name)
    {
        var sb = _pool.Get();

        try
        {
            sb.Append("hello ");
            sb.Append(name);
            return sb.ToString();
        }
        finally
        {
            _pool.Return(sb);
        }
    }
}

這種寫法更適合真實(shí)項(xiàng)目,因?yàn)槌乇旧硗ǔ?yīng)該是:

  • 單例
  • 長生命周期
  • 全局復(fù)用

從源碼心智模型看,它內(nèi)部大致是什么樣?

不用先盯實(shí)現(xiàn)細(xì)節(jié),先記住一個(gè)更重要的事實(shí):

ObjectPool 追求的是“盡量低成本地復(fù)用少量對(duì)象”,不是“做一個(gè)嚴(yán)格、復(fù)雜、功能很全的資源管理器”。

所以它的實(shí)現(xiàn)思路也很務(wù)實(shí):

  • 盡量快速取到對(duì)象
  • 盡量快速還回對(duì)象
  • 放不下就算了

也就是說,它不是那種:

  • 排隊(duì)非常嚴(yán)格
  • 絕不超量創(chuàng)建
  • 生命周期管理很復(fù)雜

的重型池模型。

這也是為什么它在熱點(diǎn)路徑里更好用:

  • 邏輯簡單
  • 開銷可控
  • 行為可預(yù)測(cè)

它適合什么場(chǎng)景?

優(yōu)先在這些場(chǎng)景里考慮它:

  • 高頻創(chuàng)建、短生命周期對(duì)象
  • 對(duì)象可以明確重置
  • 熱點(diǎn)路徑對(duì)分配和 GC 比較敏感
  • 對(duì)象復(fù)用收益明顯高于管理成本

典型例子包括:

  • StringBuilder
  • 文本拼接緩沖對(duì)象
  • 臨時(shí)解析器
  • 可重復(fù)使用的請(qǐng)求上下文輔助對(duì)象
  • 某些序列化 / 反序列化輔助對(duì)象

它不適合什么場(chǎng)景?

這部分比“怎么用”更重要,因?yàn)閷?duì)象池很容易被濫用。

1. 對(duì)象本身很輕,直接new成本極低

比如一個(gè)只有幾個(gè)字段的小對(duì)象,直接池化很可能得不償失。

因?yàn)槟阋肓耍?/p>

  • 借還成本
  • 重置成本
  • 代碼復(fù)雜度

2. 對(duì)象狀態(tài)復(fù)雜,很難可靠重置

如果對(duì)象里帶著很多內(nèi)部狀態(tài)、外部引用、事件、句柄、線程上下文,那池化風(fēng)險(xiǎn)會(huì)明顯上升。

這時(shí)候最大的風(fēng)險(xiǎn)不是“沒優(yōu)化到”,而是:

  • 臟狀態(tài)泄漏到下一次使用

3. 對(duì)象會(huì)長時(shí)間被占用

對(duì)象池最適合的是:

  • 短借短還

如果對(duì)象拿走以后要用很久,那池化收益會(huì)越來越低。

4. 想拿它當(dāng)緩存

對(duì)象池不是為了讓對(duì)象一直留著給未來業(yè)務(wù)命中,它只是復(fù)用實(shí)例。

5. 想靠它解決并發(fā)限制

它不會(huì)因?yàn)槌卮笮∈?32,就保證系統(tǒng)同時(shí)最多只存在 32 個(gè)對(duì)象。

不夠用的時(shí)候,它還是會(huì)繼續(xù)創(chuàng)建。

使用時(shí)最容易踩的坑

1. 忘記歸還

最直接,也最常見。

所以建議形成固定寫法:

var item = pool.Get();
try
{
    // use item
}
finally
{
    pool.Return(item);
}

2. 歸還前沒重置干凈

這會(huì)直接導(dǎo)致臟數(shù)據(jù)串到下一次調(diào)用。

3. 歸還后繼續(xù)使用對(duì)象

這是很危險(xiǎn)的一類 bug。

一旦對(duì)象已經(jīng)回池,它理論上隨時(shí)可能被別人再次借走。

4. 池化大對(duì)象,但不控制膨脹

有些對(duì)象會(huì)隨著業(yè)務(wù)輸入越長越大。

這時(shí)如果不做策略控制,池里可能慢慢堆滿“已經(jīng)膨脹過的大對(duì)象”,反而拉高常駐內(nèi)存。

一個(gè)比較務(wù)實(shí)的判斷標(biāo)準(zhǔn)

要不要上 ObjectPool,一般先看四件事:

  1. 這個(gè)對(duì)象是不是熱點(diǎn)路徑里高頻創(chuàng)建?
  2. 創(chuàng)建和回收成本是不是已經(jīng)值得關(guān)注?
  3. 它能不能被可靠重置?
  4. 池化帶來的復(fù)雜度,值不值得這點(diǎn)收益?

如果這四個(gè)問題里,有兩個(gè)以上答不上來,那通常先別急著池化。

總結(jié)

ObjectPool 最值得理解的,不是 API 有多簡單,而是它背后的取舍:

  • 用少量額外管理成本
  • 換熱點(diǎn)路徑上的更少分配和更低 GC 壓力

所以它真正適合的是:

  • 高頻
  • 短生命周期
  • 可重置
  • 復(fù)用收益明顯

一句話收尾:

ObjectPool 不是“讓對(duì)象永遠(yuǎn)不創(chuàng)建”,而是“讓適合復(fù)用的對(duì)象別老是重復(fù)創(chuàng)建”。

到此這篇關(guān)于C#.NET ObjectPool 對(duì)象復(fù)用、池化策略與使用邊界的文章就介紹到這了,更多相關(guān)C# ObjectPool 對(duì)象復(fù)用和池化策略內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • WPF實(shí)現(xiàn)平面三角形3D運(yùn)動(dòng)效果

    WPF實(shí)現(xiàn)平面三角形3D運(yùn)動(dòng)效果

    這篇文章主要為大家詳細(xì)介紹了WPF實(shí)現(xiàn)平面三角形3D運(yùn)動(dòng)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-09-09
  • C#用NPOI導(dǎo)出導(dǎo)入Excel幫助類

    C#用NPOI導(dǎo)出導(dǎo)入Excel幫助類

    這篇文章主要為大家詳細(xì)介紹了C#?NPOI導(dǎo)出導(dǎo)入Excel幫助類,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • 基于WPF實(shí)現(xiàn)顏色選擇器控件

    基于WPF實(shí)現(xiàn)顏色選擇器控件

    這篇文章主要介紹了如何基于WPF實(shí)現(xiàn)簡單的顏色選擇器控件,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)或工作有一定幫助,需要的小伙伴可以參考一下
    2023-08-08
  • C#操作Excel相關(guān)方法總結(jié)

    C#操作Excel相關(guān)方法總結(jié)

    這篇文章主要介紹了C#操作Excel相關(guān)方法總結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • C#開啟線程的四種示例

    C#開啟線程的四種示例

    這篇文章主要介紹了C#開啟線程的四種方法,幫助大家更好的理解和使用c#,感興趣的朋友可以了解下
    2020-10-10
  • unity實(shí)現(xiàn)鼠標(biāo)跟隨(ITween)

    unity實(shí)現(xiàn)鼠標(biāo)跟隨(ITween)

    這篇文章主要為大家詳細(xì)介紹了unity實(shí)現(xiàn)鼠標(biāo)跟隨,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-04-04
  • c# 剔除sql語句''尾巴''的五種方法

    c# 剔除sql語句''尾巴''的五種方法

    這篇文章主要介紹了c# 剔除sql語句'尾巴'的五種方法,
    2020-11-11
  • C#如何刪除指定文件或文件夾

    C#如何刪除指定文件或文件夾

    這篇文章主要介紹了C#如何刪除指定文件或文件夾,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • C#多線程系列之多階段并行線程

    C#多線程系列之多階段并行線程

    本文詳細(xì)講解了C#多線程的多階段并行線程,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-02-02
  • C#編程實(shí)現(xiàn)向并口設(shè)備發(fā)送指令、獲取并口設(shè)備的狀態(tài)

    C#編程實(shí)現(xiàn)向并口設(shè)備發(fā)送指令、獲取并口設(shè)備的狀態(tài)

    這篇文章主要介紹了C#編程實(shí)現(xiàn)向并口設(shè)備發(fā)送指令、獲取并口設(shè)備的狀態(tài),本文直接給出實(shí)例代碼,需要的朋友可以參考下
    2015-06-06

最新評(píng)論

桦南县| 正阳县| 揭东县| 双峰县| 海原县| 临潭县| 攀枝花市| 沽源县| 名山县| 双峰县| 偏关县| 赣榆县| 鸡西市| 营山县| 玛纳斯县| 平舆县| 疏勒县| 方山县| 合山市| 乾安县| 黑河市| 东山县| 大化| 图片| 江西省| 尼木县| 乌拉特后旗| 土默特左旗| 家居| 承德市| 巴青县| 浦北县| 阿拉善盟| 岳阳市| 虎林市| 比如县| 新绛县| 银川市| 中阳县| 邛崃市| 石河子市|