当前位置: 首页 > news >正文

【C#】用队列构建一个对象池管理对象的创建和释放

背景

最近在做一个图像处理的 WPF 项目,底层使用 Halcon 的 HObject 来存放图像。为了减少频繁创建和释放对象带来的开销,我实现了一个对象池,用来存放 HObject,方便后续流程复用。

最初的实现用的是 .NET 自带的 Queue<T>

private readonly Queue<T> objects = new Queue<T>();

配合 lock 实现线程安全,在 GetObject 时取出一个对象,ReturnObject 时放回队列。

由于代码会有并发情况,所以直接将Queue 改成了 ConcurrentQueue,就可以不用使用lock了


Queue vs ConcurrentQueue 对比

特性QueueConcurrentQueue
线程安全❌ 需要手动加 lock✅ 内置线程安全
性能在单线程或低并发下更快在多线程下更优,避免锁竞争
操作方式Enqueue / DequeueEnqueue / TryDequeue
Count 属性精确(单线程)近似值(多线程下可能不是实时)
适用场景单线程队列或少量锁保护的情况高并发读写队列、生产者-消费者模式

改造过程

我将原先的 Queue<T> 换成了 ConcurrentQueue<T>,并去掉了多余的 lock

原始版本(Queue + lock)

public T GetObject()
{lock (objects){if (objects.Count > 0)return objects.Dequeue();elsereturn new T();}
}

改造版本(ConcurrentQueue)

public T GetObject()
{if (objects.TryDequeue(out var obj)){return obj;}return new T();
}

改造后的好处

  1. 线程安全更自然
    ConcurrentQueue 内部使用了无锁算法,减少了阻塞等待的情况。

  2. 代码更简洁
    不再需要手动加 lock,也避免了忘记加锁导致的潜在 bug。

  3. 性能在多线程下更优
    多个线程可以同时安全地读写队列。


需要注意的坑

改造后,我也踩了几个坑:

  1. 不要先判断 Count 再操作
    在多线程下,Count 只是一个快照值。
    如果写成:

    if (objects.Count > 0)objects.TryDequeue(out var obj);
    

    就可能在判断到取出之间,队列已经被别的线程清空,导致逻辑不一致。

    正确写法:直接用 TryDequeue 判断并取值。

  2. Count 在容量控制上的延迟
    当多个线程同时 ReturnObject,可能短暂超过 _maxPoolSize
    我的处理方式是用 while 循环清理多余对象:

    while (objects.Count > _maxPoolSize && objects.TryDequeue(out var old))
    {if (old is IDisposable disposable)disposable.Dispose();
    }
    

    虽然会有一点“超限再回落”,但影响不大。


关于 HObject 的思考

在改造过程中,我发现 HObject 这种一次性资源(Dispose 后不可复用)其实不太适合放到传统意义的“对象池”里。
但是我为了自动化释放管理,同时不愿意立马释放当前图像,所以这么做了。


总结

从这次改造中,我有几点心得:

  • 如果是高并发的队列操作,ConcurrentQueue 是更优解,省去了手动加锁的麻烦。
  • 多线程下不要依赖 Count 做逻辑判断,直接用 TryDequeue 更安全。
  • 改造代码时,不要只关注语法,还要考虑资源生命周期,否则会出现“对象提前被释放”的问题。

💡 一句话总结

在多线程队列管理上,ConcurrentQueue 是比 Queue+lock 更简洁的选择,但资源的生命周期管理才是对象池真正的难点。

http://www.lryc.cn/news/618809.html

相关文章:

  • PySpark性能优化与多语言选型讨论
  • 各种 dp 刷题下
  • 人机交互:连接人类与数字世界的桥梁
  • apache+虚拟主机
  • 五、Elasticsearch在Linux的安装部署
  • Rust 项目编译故障排查:从 ‘onnxruntime‘ 链接失败到 ‘#![feature]‘ 工具链不兼容错误
  • 使用reqwest+select实现简单网页爬虫
  • Rust 性能提升“最后一公里”:详解 Profiling 瓶颈定位与优化|得物技术
  • open-webui源码分析1—文件上传
  • Vue接口平台十三——测试记录
  • springboot整合sharding-jdbc 5.5.2 做单库分表
  • 燕山大学计算机网络实验(2025最新)
  • Java调用Vue前端页面生成PDF文件
  • 深入剖析 React 合成事件:透过 onClick 看本质
  • Java 工厂方法模式
  • Flask + Vue.js 物联网数字大屏实现方案
  • 数据分析基本内容(第二十节课内容总结)
  • Rsync自动化备份平台建设实战
  • 【数据分析与挖掘实战】金融风控之贷款违约预测
  • 阿里云 Windows 服务器 搭建 Gitea 私有 Git 服务器完整教程
  • 开疆智能Ethernet转ModbusTCP网关连接PAC3200电能表配置案例
  • VirtualBox 虚拟机磁盘扩容完整手册
  • MaxKB+合合信息TextIn:通过API实现PDF扫描件的文档审核
  • [git] 重配ssh key | 解决冲突
  • python日志中的logging.basicConfig和logging.getLogger
  • [Robotics_py] 机器人运动模型 | `update`函数 | 微积分矩阵
  • 数据类型 list
  • 浏览器CEFSharp+X86+win7 之 全球外贸电商平台订单管理(十)
  • 每日五个pyecharts可视化图表-line:从入门到精通 (4)
  • 数据结构:链表栈的操作实现( Implementation os Stack using List)