atomic<shared_ptr<>>内部工作原理究竟如何?技术深度问询
你关注的核心点非常明确:atomic<shared_ptr<T>> 如何保证两个指针(数据指针+控制块指针)的原子性更新,而非shared_ptr本身的控制块机制。我们直接拆解你给出的VC++ store 实现代码,逐行解析其逻辑:
核心背景
shared_ptr 本质是两个指针的组合:指向数据的_Ptr,和指向控制块的_Rep。atomic操作必须保证这两个指针的更新是原子的——其他线程要么看到完整的旧指针对,要么看到完整的新指针对,绝不能看到"旧数据指针+新控制块"或者反过来的中间状态。
逐行解析VC++的store实现
void store(shared_ptr<_Ty> _Value, const memory_order _Order = memory_order_seq_cst) noexcept { _Check_store_memory_order(_Order); const auto _Rep = this->_Repptr._Lock_and_load(); remove_extent_t<_Ty>* const _Tmp = _Value._Ptr; _Value._Ptr = this->_Ptr.load(memory_order_relaxed); this->_Ptr.store(_Tmp, memory_order_relaxed); this->_Repptr._Store_and_unlock(_Value._Rep); _Value._Rep = _Rep; }
_Check_store_memory_order(_Order);
校验传入的内存序合法性,比如禁止传入memory_order_consume这类不适合store操作的内存序,避免非法内存语义。const auto _Rep = this->_Repptr._Lock_and_load();
这里的_Repptr是VC++对控制块指针的原子封装。_Lock_and_load会先加锁(通常是自旋锁,适配atomic操作的轻量级同步需求),然后加载当前atomic<shared_ptr>持有的旧控制块指针。加锁的目的是:在整个store操作期间,其他线程不能修改这个atomic<shared_ptr>的状态,保证两个指针的更新是原子的。remove_extent_t<_Ty>* const _Tmp = _Value._Ptr;
临时保存要写入的新数据指针,因为后续会修改传入的_Value对象,避免丢失新数据指针。_Value._Ptr = this->_Ptr.load(memory_order_relaxed);
把当前atomic<shared_ptr>里的旧数据指针,替换到传入的_Value的_Ptr字段中。用memory_order_relaxed是因为这里只是临时借用_Value的结构,不需要内存同步——真正的同步会在控制块的操作中完成。this->_Ptr.store(_Tmp, memory_order_relaxed);
将新数据指针写入atomic<shared_ptr>的_Ptr字段。同样用relaxed,因为数据指针和控制块指针是强绑定的,只要控制块的更新是同步的,数据指针的可见性会被控制块的内存屏障覆盖。this->_Repptr._Store_and_unlock(_Value._Rep);
这是整个操作的核心同步点:将新的控制块指针(来自传入的_Value)原子写入_Repptr,然后解锁。解锁操作会根据传入的_Order插入对应的内存屏障(比如memory_order_seq_cst会插入全屏障),保证所有线程看到的指针对是一致的。_Value._Rep = _Rep;
把之前加载的旧控制块指针还给_Value。此时_Value的_Ptr是旧数据指针,_Rep是旧控制块指针,完全还原成了当前atomic<shared_ptr>更新前的状态。当_Value离开作用域时,会自动递减旧控制块的引用计数,完成旧资源的生命周期管理——这一步巧妙地利用传入的参数处理旧引用,避免了手动管理引用计数的风险。
实现逻辑总结
VC++的atomic<shared_ptr>实现核心思路是:
- 用锁模拟128位原子操作:因为多数硬件不支持同时原子更新两个64位指针,所以通过自旋锁保证两个指针的更新过程是原子的,不会被其他线程打断。
- 借参数容器管理旧引用:利用传入的
shared_ptr参数临时保存旧指针对,自动完成旧控制块的引用计数递减,避免内存泄漏。 - 内存序集中在控制块操作:数据指针的操作用relaxed减少同步开销,所有内存屏障都在控制块的解锁/存储步骤中完成,保证指针对的可见性一致性。
内容的提问来源于stack exchange,提问作者Edison von Myosotis

