Lock-free可交换对象池缓存:运行时池交换下无锁get、recycle操作实现咨询
现有实现的核心缺陷
你当前的代码存在一个致命的生命周期安全问题:_atomicDefaultRulePool存储的是PreparedRulePool的裸指针,你在atomicSwapPool中替换原子指针后直接销毁旧池所持有的shared_ptr,如果此时有其他线程已经加载了旧的裸指针、正在执行get或recycle逻辑,会直接访问野指针,导致程序崩溃。
可行的无锁实现方案
你不需要放弃无锁设计,只要调整生命周期管理逻辑即可解决问题:
- 把
_atomicDefaultRulePool的类型从std::atomic<裸指针>替换为std::atomic<std::shared_ptr<detail::PreparedRulePool<Data, Delegate>>>,C++20及以上标准原生支持该原子类型的无锁操作,低版本可引入Boost库的atomic_shared_ptr达到相同效果。 getPreparedDefaultRule接口直接原子加载shared_ptr,该操作会自动延长池的生命周期,哪怕加载后池立刻被替换,你手里的shared_ptr指向的旧池依然有效,调用内部无锁的getPreparedRule完全安全,全程没有加锁操作。recyclePreparedDefaultRule接口同样先原子加载池的shared_ptr,你原有的tag匹配逻辑可以保留:如果当前池的tag和待回收对象的tag一致就回收到池,不一致直接让unique_ptr自动析构对象即可,旧池没有必要再回收对象,等所有持有的旧池shared_ptr都释放后,旧池会自动销毁。- 池替换逻辑直接用
std::atomic_exchange操作原子shared_ptr即可,不需要单独维护额外的_defaultRulePool成员,原子shared_ptr本身会自动管理旧池的生命周期,不会提前销毁正在被使用的旧池。
修正后的代码示例
template <typename Data, typename Delegate> class DefaultRulePoolCache { public: DefaultRulePoolCache() = default; std::unique_ptr<detail::PreparedRule<Data, Delegate>> getPreparedDefaultRule() { // 原子加载共享指针,自动延长池生命周期,全程无锁 auto current_pool = _atomicDefaultRulePool.load(); if (!current_pool) { return nullptr; } return current_pool->getPreparedRule(); } void recyclePreparedDefaultRule(std::unique_ptr<detail::PreparedRule<Data, Delegate>> preparedRule) { if (!preparedRule) { return; } // 原子加载共享指针,全程无锁 auto current_pool = _atomicDefaultRulePool.load(); if (!current_pool) { return; } // tag匹配才回收,否则直接释放对象 if (current_pool->getTag() == preparedRule->getTag()) { current_pool->recyclePreparedRule(std::move(preparedRule)); } } void setDefaultRulePool(std::shared_ptr<detail::PreparedRulePool<Data, Delegate>> defaultRulePool) { std::lock_guard<std::mutex> lock(_op_mutex); // 原子替换池,旧池会等所有引用释放后自动销毁 _atomicDefaultRulePool.exchange(std::move(defaultRulePool)); } void removeDefaultRulePool() { std::lock_guard<std::mutex> lock(_op_mutex); _atomicDefaultRulePool.exchange(nullptr); } private: std::atomic<std::shared_ptr<detail::PreparedRulePool<Data, Delegate>>> _atomicDefaultRulePool{nullptr}; // 仅用于保证set/remove操作的串行化,不影响get/recycle的无锁逻辑 std::mutex _op_mutex; };
如果你的编译环境不支持C++20的原子shared_ptr,可以把原子操作替换为std::atomic_load、std::atomic_exchange等自由函数操作非原子的shared_ptr成员,性能会略低但依然是线程安全的。
内容的提问来源于stack exchange,提问作者benjist
相关产品推荐
相关产品推荐

