C++ 通用对象池的设计与实现

本文深入探讨了对象池的设计与实现,包括其在内存管理和线程安全方面的策略。对象池通过复用对象减少创建销毁的开销,采用std::deque作为底层容器,使用双端锁来保证并发安全。文中详细阐述了扩容和缩容机制,以及在这些操作中如何优化锁的使用以提高性能。此外,还讨论了对象的验证和异常处理策略,为读者提供了实现高效对象池的全面指导。
摘要由CSDN通过智能技术生成

用语:
client:使用对象池的线程
pool:对象池
deque:对象池的容器
base-object:初始化时client传入的基本对象
object:对象池的存储对象
size:池内剩余object个数
total_size:池所创建的对象个数(包括被取走的object)

1. 功能概要

池一般用于复杂对象、高消耗对象的复用,基于这个观点,基本使用如下:

  1. client初始化一个基本对象base-object,该对象的创建过程可以是极其复杂的。
  2. client初始化pool对象,并将base-object传入,可以指定pool的capacity(default=16)
  3. pool利用base-object,复制base_size个object放入池中
  4. client使用pool.get()获取object,默认获取不到则等待(不会出现饥饿情况,存在等待时,必定在扩容中)
  5. client使用pool.release()将用完的object放回池中

其他功能:

  1. client可以在初始化pool的时候传入一个函数指针,该函数用于object可用性校验(default=非空校验)
  2. client可以在初始化pool的时候传入一个函数指针,该函数用于非法object如何放回池中(default=删除非法object,使用base-object新建一个)
  3. pool.get(time,strategy),get过程最多等待time时长,strategy指定超时后的处理策略。提供两种基本策略:直接返回nul、新建一个对象返回

2. 底层容器

底层容器 std::deque

deque底层是vector+buffer的形式。

在这里插入图片描述

由于中控器是vector,因此,在vector未满的时候,扩容是buffer扩容,使用单端锁控制并发即可。
如果vector满了,会使用vector的扩容机制(开辟新的空间、复制进去、释放旧的空间),这时候需要锁住容器。

3. 锁机制

锁机制:双端锁

队列拥有两把锁,入队锁和出队锁。

入队时,需要需要获取入队锁,入队成功将唤醒一个出队线程。

出队时,需要获取出队锁。
如果此时对象不足,会阻塞等待入队线程唤醒。

扩容时,获取双端锁(具体查看扩容机制)。
缩容时,获取双端锁(具体查看缩容机制)。

非扩容情况,双端锁的合理性分析:
双端锁对应的资源实际是不同指针(begin和end),也是不同空间(back空间和front空间)。

考虑边界情况:

  • 如果资源对象只有一个,同时发生get和release,get会获得front的对象,release会在back空间放入一个新的对象,没有冲突发生。

  • 如果资源对象一个也没有,同时发生get和release,get会被阻塞,release放入后唤醒get;或者release放入,get成功获取对象。

  • begin和end是左开右闭区间,指向的是不同区域

注意:实际上要求容器的size是atomic的,不然同时双端操作会导致size操作不安全

4. 扩容机制

扩容机制:
pool使用逻辑扩容,物理扩容交给std::deque自己实现。

逻辑扩容:采用渐进式扩容,具体如下:
触发扩容阈值的时候,获取双端锁,然后创建少量的新对象放入池中,释放双端锁。
然后在后续每次get的时候,新建1个对象放进池中,直到达到新的扩容目标个数。

这样把新建对象的消耗分散到各个时期,并减少了扩容获取双端锁的时间。

如果扩容的时候,发现上次渐进扩容未完成,则直接创建“剩余所需渐进创建对象个数+base_size/2个”对象。

5. 缩容机制

缩容分两种,一种是删除池内对象,另一种是reserve deque。

由于池内对象可以看做无状态的base-object,删除池内对象时,pop_front即可。
deque底层是vector,因此reserve需要双端锁。

惰性缩容:get时发现池内剩余object个数大于阈值时进行缩容。
定期缩容:存在一个计时器,记录get的上次调用时间,若长时间没有client调用get,则触发缩容。

单独的定期缩容:假如经过多次扩容之后,只剩一个线程在访问,每次访问都刷新了定时器,会导致定期缩容无法触发。

单独的惰性缩容:如果没有client进行get,会导致资源空占。

如果缩容的时候渐进扩容还没有完成,直接取消渐进扩容,重置相应属性。

6. 优化空间

发生扩容、缩容的时候,需要获得双端锁,会使整个Pool无法对外工作,但是不一定每次都需要获得双端锁。

6.1 扩容优化思路

扩容分两种情况,其中一种情况是向pool中增加object,不涉及reserve(不触发vector扩容),此时只需要获得入队锁。

6.2 缩容优化思路

缩容分两种情况,其中一种情况是从pool中移除object,不涉及reserve(不触发vector缩容),此时只需要获得出队锁。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值