OmenSuperHub终极指南:3步解锁惠普OMEN游戏本隐藏性能的免费方案
2026/6/15 16:59:36
ResourcePool实现了一个基于智能指针自定义删除器(Deleter)机制的高性能对象池。
其核心思想是:当用户从池中获取对象时,得到的是一个std::shared_ptr。当这个智能指针引用计数归零(离开作用域)时,不会直接delete内存,而是触发自定义删除器,将对象“归还”给对象池。
测试代码和使用示例在test_resourcePool.cpp。
代码中主要涉及三个类,它们的关系如下:
ResourcePool_l<C>(内部实现类)std::enable_shared_from_this。这是为了在创建对象时,能够安全地将自身的weak_ptr传递给发出的智能指针,以便对象析构时能找到回家的路。_objs:std::vector<C*>,存储空闲对象的栈。_busy:std::atomic_flag,用于实现轻量级的自旋锁(Spinlock)。_alloc: 对象分配器 lambda,默认使用new C()。getPtr和recycle中,使用_busy.test_and_set()。如果获取锁失败(说明有其他线程正在操作池),它不会阻塞等待,而是直接new一个新对象(获取时)或直接delete对象(回收时)。这种设计牺牲了对池大小的严格控制,换取了极高的并发性能(避免线程挂起)。ResourcePool<C>(对外接口类/外观模式)ResourcePool_l的代理(Facade)或包装器。ResourcePool内部持有一个std::shared_ptr<ResourcePool_l<C>> pool。ResourcePool_l必须以shared_ptr形式存在才能使用shared_from_this()。ResourcePool隐藏了这一细节,让用户可以像使用普通栈对象一样声明ResourcePool。obtain、setSize等调用转发给内部的pool。shared_ptr_imp<C>(增强型智能指针)ResourcePool::obtain()返回的智能指针类型。std::shared_ptr<C>。_quit标志(atomic_bool)。quit()方法,允许用户在持有对象期间,决定该对象销毁时是归还池中还是彻底删除。obtain/getPtr)ResourcePool::obtain()。ResourcePool_l::obtain()。ResourcePool_l::getPtr():_busy。_objs有库存,弹出并返回;否则调用_alloc()新建。释放锁。_alloc()新建返回。shared_ptr_imp,并将ResourcePool_l的weak_ptr传入删除器中。shared_ptr_imp引用计数归零,触发删除器lambda。weakPool.lock()是否能获取到ResourcePool_l的强引用。delete ptr。strongPool->recycle(ptr)。ResourcePool_l::recycle(ptr):emplace_back入队;如果池满了,delete ptr。释放锁。delete ptr。ResourcePool是面向用户的句柄,负责管理ResourcePool_l的生命周期;ResourcePool_l是实际干活的实体。atomic_flag实现无锁(Wait-free)的尝试机制,遇到竞争立即降级为直接分配/释放,杜绝了线程阻塞。weak_ptr解决循环引用和悬垂指针问题。如果池先于对象销毁,对象析构时会安全地自我删除。obtain返回shared_ptr_imp,功能更强(支持quit()放弃回收),但对象体积稍大(多一个_quit指针)。 *obtain2返回标准shared_ptr,更轻量,但无法中途控制是否放弃回收_objs,为什么使用vector类型而不是List?在ResourcePool_l中使用std::vector<C *>而不是List(或std::list),主要是出于极致性能的考虑,特别是为了配合其自旋锁 (Spinlock)和Fail-fast的设计策略。
ResourcePool_l使用了std::atomic_flag(_busy) 来实现一个非阻塞的自旋锁。
test_and_set失败(锁被占用),代码不会等待,而是直接降级为new或delete对象。对比两种容器的操作耗时:
std::vector:pop_back(): 仅仅是将内部的size计数器减 1。这是纯 CPU 指令操作,耗时在纳秒级。emplace_back(): 在预留空间足够的情况下(_objs.reserve已调用),仅仅是将指针写入数组并size加 1。也是纳秒级。std::list/List:pop_back(): 需要释放链表节点的内存(调用free或delete)。内存分配器本身通常包含锁,且操作耗时远高于简单的指针运算。push_back(): 需要分配新的链表节点内存(调用malloc或new)。结论:使用vector可以将临界区缩小到仅包含几条汇编指令,极大降低了多线程竞争导致“获取锁失败”的概率。
std::list: 每次将对象放回池中(recycle),都需要为链表节点本身分配一次内存;每次取出对象,都要释放节点内存。这违背了对象池“减少内存分配”的初衷。std::vector:setSize调用了_objs.reserve(size)。recycle中有判断if (_objs.size() >= _pool_size)。vector的底层数组一旦分配,在运行过程中几乎永远不需要重新分配或扩容。它仅仅是在一段连续内存上移动尾部指针。std::vector内部存储的是C*指针数组,这块内存在物理上是连续的。CPU 缓存(L1/L2 Cache)对连续内存的访问非常友好。std::list的节点分散在堆内存的各个角落,遍历或访问时容易产生 Cache Miss。back()(取栈顶) 和pop_back()(出栈) 以及emplace_back()(入栈)。std::vector是实现栈结构性能最好的容器(比std::stack适配器更直接)。