用语:
client:使用对象池的线程
pool:对象池
deque:对象池的容器
base-object:初始化时client传入的基本对象
object:对象池的存储对象
size:池内剩余object个数
total_size:池所创建的对象个数(包括被取走的object)
1. 功能概要
池一般用于复杂对象、高消耗对象的复用,基于这个观点,基本使用如下:
- client初始化一个基本对象base-object,该对象的创建过程可以是极其复杂的。
- client初始化pool对象,并将base-object传入,可以指定pool的capacity(default=16)
- pool利用base-object,复制base_size个object放入池中
- client使用pool.get()获取object,默认获取不到则等待(不会出现饥饿情况,存在等待时,必定在扩容中)
- client使用pool.release()将用完的object放回池中
其他功能:
- client可以在初始化pool的时候传入一个函数指针,该函数用于object可用性校验(default=非空校验)
- client可以在初始化pool的时候传入一个函数指针,该函数用于非法object如何放回池中(default=删除非法object,使用base-object新建一个)
- 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缩容),此时只需要获得出队锁。