当前位置: 首页 > 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.dtcms.com/a/328132.html

相关文章:

  • NumPy 快速入门与实战教程(逐行拆解 + 专业扩展)
  • 详细了解sklearn中的CountVectorizer
  • 【c++深入系列】:万字详解模版(下)
  • [Robotics_py] 路径规划算法 | 启发式函数 | A*算法
  • MaxKB+合合信息TextIn:通过API实现PDF扫描件的文档审核
  • LRU算法与LFU算法
  • 农业智慧大屏系统 - Flask + Vue实现
  • 云计算分类与主流产品
  • iOS 文件管理全流程实战,从开发调试到数据迁移
  • 初识神经网络04——构建神经网络2
  • 软路由雷达:基于OpenWrt系统的传统逆向思路的冷门实现
  • 外部中断寄存器的实现-库函数版(STC8)
  • Android性能优化:架构层面的性能考量
  • 【第13话:泊车感知】场景检测与分割:自主泊车场景中的检测及语义分割方法
  • Linux中的tar 和crontab命令
  • 需求的透明化如何实现
  • Threejs 设置灯光照射点位置 辅助器不跟随移动
  • 基于MATLAB实现的PSO优化BP神经网络
  • Java数据结构之数组
  • 电商双 11 美妆数据分析学习报告
  • 锅气:「现炒之魂·烟火人间」
  • 【Unity】Unity中ContentSizeFitter有时无法及时自适应大小问题解决
  • Flutter 基于google验证登录实现
  • HeidiSQL 连接 MySQL 报错 10061
  • Xshell连接虚拟机密码错误解决方法
  • Ansible部署应用
  • Gradle 配置教程:与 Maven 对比详解(含完整迁移指南)
  • methods和computed的区别
  • tlias智能学习辅助系统--Maven高级-继承
  • 北京JAVA基础面试30天打卡08