探究std::weak_ptr::lock()实现:如何保证多线程无额外同步使用
根据cppreference的说明:
std::weak_ptr<T>::lock实际上会原子执行expired() ? shared_ptr() : shared_ptr(*this)。
示例代码
#include<memory> #include<thread> std::shared_ptr<int> g_s = std::make_shared<int>(1); std::weak_ptr<int> w_p{g_s}; void f1(std::weak_ptr<int>& wp) { std::shared_ptr<int> l_s1 = wp.lock(); // 读取 g_s } void f2() { std::shared_ptr<int> l_s2 = std::make_shared<int>(3); std::thread th(f1, std::ref(w_p)); th.detach(); g_s = l_s2; // 修改 g_s } int main() { std::thread(f2).join(); }
GCC中的具体实现(基于libstdc++)
在GCC的shared_ptr.h和shared_ptr_base.h头文件中,lock()的核心实现如下:
// __shared_ptr版本的lock __shared_ptr<_Tp, _Lp> weak_ptr::lock() const noexcept { return __shared_ptr<element_type, _Lp>(*this, std::nothrow); } // shared_ptr版本的lock shared_ptr<_Tp> weak_ptr::lock() const noexcept { return shared_ptr<_Tp>(*this, std::nothrow); }
关键构造逻辑解析
lock()本质是通过带std::nothrow参数的shared_ptr构造函数实现安全升级:
- 从
weak_ptr构造shared_ptr的核心逻辑
/** * @brief 构造一个与__r共享所有权的shared_ptr,并复制__r中存储的指针 * @param __r 一个weak_ptr * @post use_count() == __r.use_count() * @throw 当__r已过期时抛出bad_weak_ptr,此时构造函数无效果 */ template<typename _Yp, typename = _Constructible<const weak_ptr<_Yp>&>> explicit shared_ptr(const weak_ptr<_Yp>& __r) : __shared_ptr<_Tp>(__r) { } // 底层__shared_ptr的构造实现 template<typename _Yp, typename = _Compatible<_Yp>> explicit __shared_ptr(const __weak_ptr<_Yp, _Lp>& __r) : _M_refcount(__r._M_refcount) // 可能抛出异常 { // 由于_M_refcount(__r._M_refcount)未抛出,此时复制__r._M_ptr是安全的 _M_ptr = __r._M_ptr; }
这里的核心是先原子操作引用计数:构造_M_refcount时会尝试将弱引用计数升级为强引用计数,这一步是原子的。只有当升级成功(即对象未过期),才会复制对象指针_M_ptr,避免了指针被释放后访问的风险。
- weak_ptr的构造安全处理
针对多线程场景下weak_ptr的拷贝,GCC做了特殊处理:
// 注释说明:"直观"的转换构造函数实现存在严重问题 // __r._M_ptr可能已失效,转换时可能需要访问*__r._M_ptr(比如虚继承场景) // 多线程中__r._M_ptr随时可能失效,无法避免虚假访问违规 template<typename _Yp, typename = _Compatible<_Yp>> __weak_ptr(const __weak_ptr<_Yp, _Lp>& __r) noexcept : _M_refcount(__r._M_refcount) { _M_ptr = __r.lock().get(); }
这里先拷贝原子安全的引用计数_M_refcount,再通过lock()获取有效的对象指针,避免直接拷贝可能已失效的_M_ptr。
- 从shared_ptr构造weak_ptr的逻辑
template<typename _Yp, typename = _Compatible<_Yp>> __weak_ptr(const __shared_ptr<_Yp, _Lp>& __r) noexcept : _M_ptr(__r._M_ptr), _M_refcount(__r._M_refcount) { }
此时shared_ptr的对象肯定有效,因此可以直接拷贝指针和引用计数,无需额外安全处理。
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

