C# 对象池化方法 C#如何使用Microsoft.Extensions.ObjectPool

来源:这里教程网 时间:2026-02-21 17:40:52 作者:

ObjectPool 基本用法:从创建到归还

直接用

ObjectPool<t></t>
需要先配置一个池策略,不能 new 出来就用。最简路径是借助
DefaultObjectPoolProvider
创建带默认策略的池:

var provider = new DefaultObjectPoolProvider();
var pool = provider.Create(new DefaultPooledObjectPolicy<StringBuilder>());

获取对象用

Get()
,用完必须调用
Return()
—— 否则对象不会回到池里,等于白配:

var sb = pool.Get();
→ 拿到实例(可能新建,也可能复用)
sb.Append("hello");
→ 正常使用
pool.Return(sb);
→ 关键!不调用这句,下次
Get()
不会拿到它

自定义 PooledObjectPolicy:控制创建、验证和清理逻辑

默认策略只做

new T()
obj?.Clear()
(仅对
StringBuilder
等少数类型生效)。多数场景需要自己写策略,比如复用
MemoryStream
时得重置位置和长度:

public class MemoryStreamPolicy : IPooledObjectPolicy<MemoryStream>
{
    public MemoryStream Create() => new MemoryStream(1024);
    public bool Return(MemoryStream obj) => obj.Length == 0; // 可选校验
    public void Return(MemoryStream obj)
    {
        obj.Position = 0;
        obj.SetLength(0); // 清空内容,但保留缓冲区
    }
}

注意两点:

Return()
方法返回
bool
表示是否允许归还;返回
false
会被直接丢弃(如对象状态异常)
不要在
Return()
里调用
Dispose()
—— 池不负责释放资源,除非你明确在策略里处理

性能陷阱:池大小没限制,内存可能越攒越多

DefaultObjectPoolProvider
默认不限制池容量,空闲对象一直留着。高并发下若对象体积大(比如含大数组的类),容易吃光内存。

解决方法是传入

PoolingPolicyOptions
控制上限:

var options = new PoolingPolicyOptions
{
    MaximumRetained = 50, // 最多缓存 50 个空闲实例
};
var pool = provider.Create(new MyPolicy(), options);

另外,

MaximumRetained
是 per-pool 的,不是全局总数。每个
Create()
调用都独立计数。

常见错误:把池当成单例或线程局部存储用

有人误以为池本身是线程安全的「容器」,然后在多个线程间共享同一个

ObjectPool<t></t>
实例 —— 这没问题,
ObjectPool<t></t>
内部是线程安全的。

但错在:把池和它管理的对象混为一谈。典型错误包括:

在线程 A 中
Get()
后,把对象传给线程 B 使用,再在线程 B 中
Return()
—— 可以,但需确保对象本身线程安全(如
StringBuilder
不是)
重复
Return()
同一个对象两次 —— 会触发内部断言失败或静默忽略,取决于策略实现
async
方法中
await
后才
Return()
,中间被其他代码误用该对象

最稳妥的做法:Get 和 Return 尽量写在同一个作用域内,用

using
无法自动 Return,必须显式写。

相关推荐