ObjectPool
object_pool.hpp 在当前主线中提供了一组基于“空闲索引队列”的 RAII 槽池接口,核心模板为:
LibXR::BasicObjectPool<Data, FreeQueue>
以及三个当前直接公开的别名:
LibXR::ObjectPool<Data, IndexType>:底层使用Queue<IndexType>LibXR::SPSCObjectPool<Data, IndexType>:底层使用SPSCQueue<IndexType>LibXR::MPMCObjectPool<Data, IndexType>:底层使用MPMCQueue<IndexType>
与 LockFreePool 不同,这一组对象池强调的是:
- 通过
Acquire()获取一个独占槽位; - 通过 move-only
Handle在析构时自动归还槽位; - 上层直接在槽位对象上原地构造和修改业务数据。
1. 设计要点
1.1 最小队列约束 PoolIndexQueue
BasicObjectPool 不依赖某一个具体队列类型,而是要求底层空闲索引队列满足最小 typed 接口:
ValueTypePush(const ValueType&)Pop(ValueType&)Size()
因此当前主线可以直接复用普通 Queue、SPSCQueue、MPMCQueue 作为空闲索引管理器。
1.2 Move-only Handle
成功获取槽位后,对象池不会直接返回裸指针,而是返回一个 move-only Handle:
Handle析构时会自动把槽位索引归还给对象池;- 禁止拷贝,避免同一个槽位被多个句柄同时持有;
- 支持
Get()、operator->()、operator*()访问槽内对象; - 支持
Index()查询当前槽位索引; - 支持
Reset()主动提前归还。
这也是“RAII 对象池”这个名称的来源。
2. 当前主线提供的构造方式
BasicObjectPool 当前支持四类构造方式:
- 内部 queue + 内部 slots
- 内部 queue + 外部 slots
- 外部 queue + 内部 slots
- 外部 queue + 外部 slots
选择含义:
- 需要最省心的用法:直接用内部 queue / 内部 slots。
- 需要把槽位放在调用方控制的存储区:使用外部
slots。 - 需要复用已有队列实现或精确控制队列行为:使用外部
free_queue。
当使用外部 free_queue 时,当前实现要求:
- 队列在传入时必须为空;
- 队列只供当前 pool 独占使用;
- 队列容量至少能容纳
slot_count个索引。
3. 主要接口
3.1 获取与归还
ErrorCode Acquire(Handle& handle)void Handle::Reset()
行为要点:
- 成功时返回
ErrorCode::OK; - 无空闲槽位时返回底层队列的弹出失败结果,当前常见表现为
ErrorCode::EMPTY; Handle析构时会自动归还槽位,不必手工把索引放回队列。
3.2 容量查询
size_t EmptySize() const:当前仍可获取的空闲槽位数size_t Size() const:对象池总槽位数
3.3 非所有权访问
Data& UnsafeAt(size_t index)const Data& UnsafeAt(size_t index) const
这两个接口会绕过 Acquire() / Handle 的所有权语义,只适合调试、检查外部存储区,或调用方明确知道槽位状态的场景。
4. 三个常用别名
4.1 ObjectPool
template <typename Data, typename IndexType = uint32_t>
using ObjectPool = BasicObjectPool<Data, Queue<IndexType>>;
适合普通线程上下文中、对锁自由度没有额外要求的通用对象池场景。
4.2 SPSCObjectPool
template <typename Data, typename IndexType = uint32_t>
using SPSCObjectPool = BasicObjectPool<Data, SPSCQueue<IndexType>>;
适合明确是单生产者 / 单消费者的空闲槽位管理路径。
4.3 MPMCObjectPool
template <typename Data, typename IndexType = uint32_t>
using MPMCObjectPool = BasicObjectPool<Data, MPMCQueue<IndexType>>;
适合多生产者 / 多消费者并发获取与归还槽位的场景。
5. 使用示例
#include <libxr.hpp>
struct Packet
{
uint32_t id = 0;
uint8_t payload[32] = {};
};
LibXR::ObjectPool<Packet> pool(16);
LibXR::ObjectPool<Packet>::Handle handle;
if (pool.Acquire(handle) == LibXR::ErrorCode::OK)
{
handle->id = 42;
(*handle).payload[0] = 0xAA;
}
// handle 离开作用域后自动归还槽位
如果需要提前归还:
handle.Reset();
6. 与 LockFreePool 的区别
ObjectPool系列以“槽位独占 + RAII 归还”为核心语义,更适合对象复用、临时缓冲区借还、工作槽租赁。LockFreePool以“槽状态机 + 无序 Put/Get”为核心语义,更适合高并发缓存和无严格配对的投递/取走。
如果你的场景需要“拿到一个槽位,期间独占修改,结束后自动归还”,通常优先选择 ObjectPool 系列。