跳到主要内容

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 接口:

  • ValueType
  • Push(const ValueType&)
  • Pop(ValueType&)
  • Size()

因此当前主线可以直接复用普通 QueueSPSCQueueMPMCQueue 作为空闲索引管理器。

1.2 Move-only Handle

成功获取槽位后,对象池不会直接返回裸指针,而是返回一个 move-only Handle

  • Handle 析构时会自动把槽位索引归还给对象池;
  • 禁止拷贝,避免同一个槽位被多个句柄同时持有;
  • 支持 Get()operator->()operator*() 访问槽内对象;
  • 支持 Index() 查询当前槽位索引;
  • 支持 Reset() 主动提前归还。

这也是“RAII 对象池”这个名称的来源。


2. 当前主线提供的构造方式

BasicObjectPool 当前支持四类构造方式:

  1. 内部 queue + 内部 slots
  2. 内部 queue + 外部 slots
  3. 外部 queue + 内部 slots
  4. 外部 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 系列。