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

1次阅读

ObjectPool需先配置策略再使用,通过DefaultObjectPoolProvider创建池,Get获取、Return归还对象;自定义PooledObjectPolicy可控制创建、验证与清理;注意MaximumRetained限制池大小防内存泄漏;池线程安全,但对象需确保线程安全且不可重复Return。

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

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

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

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

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

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

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

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

public class MemoryStreamPolicy : IPooledObjectPolicy {     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 实例 —— 这没问题,ObjectPool 内部是线程安全的。

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

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

最稳妥的做法:Get 和 Return 尽量写在同一个作用域内,用 using 无法自动 Return,必须显式写。

text=ZqhQzanResources