当前位置: 首页 > news >正文

深入理解 shared_ptr 与 weak_ptr:访问控制与线程安全

深入理解 shared_ptrweak_ptr:访问控制与线程安全

引言

在多线程编程中,合理使用智能指针(如 std::shared_ptrstd::weak_ptr)对于管理资源的生命周期和避免潜在的竞争条件至关重要。本文将通过两个版本的 main 函数示例来探讨如何利用 weak_ptr 实现对对象的安全访问,以及如何演示访问成功与失败的情况。

示例代码概览

我们将定义一个简单的类 A,它包含一个成员函数 testA。此外,我们还会创建一个子线程函数 handler01,该函数接收一个 weak_ptr<A> 参数,并尝试访问 A 对象。最后,我们将展示两个版本的 main 函数,分别对应于可以访问和无法访问 A 对象的情形。

类定义与子线程函数
#include <iostream>#include <memory>#include <thread>#include <chrono>​using namespace std;​class A {public:void testA() {cout << "testA function called" << endl;}};​// 子线程void handler01(weak_ptr<A> pw) {std::this_thread::sleep_for(std::chrono::seconds(2));// 使用 weak_ptr 转换为 shared_ptr 进行安全访问shared_ptr<A> sp = pw.lock();if (sp != nullptr) {sp->testA();} else {cout << "A对象已经析构," << endl;}}
版本一:能够访问 A 对象

在这个版本中,主线程等待的时间足够长,使得子线程有机会在 A 对象被销毁之前访问它。

int main() {{shared_ptr<A> p(new A());thread t1(handler01, weak_ptr<A>(p));​// 主线程等待足够长时间确保子线程可以访问A对象std::this_thread::sleep_for(std::chrono::seconds(3)); // 等待时间延长​// 阻塞等待子线程结束t1.join();}​return 0;}

输出结果

testA function called

解释

  • 在这个版本中,主线程等待了足够长的时间(3秒),使得子线程能够在 A 对象被销毁前锁定并调用 testA 方法。

  • 输出显示子线程成功调用了 testA 方法。

版本二:无法访问 A 对象

在这个版本中,主线程等待的时间较短,导致子线程尝试访问时 A 对象已经被销毁。

int main() {{shared_ptr<A> p(new A());thread t1(handler01, weak_ptr<A>(p));​// 主线程等待一段时间后释放A对象std::this_thread::sleep_for(std::chrono::milliseconds(500)); // 缩短等待时间p.reset(); // 释放A对象​// 阻塞等待子线程结束t1.join();}​return 0;}

输出结果

A对象已经析构,

解释

  • 在这个版本中,主线程只等待了很短的时间(500毫秒),然后就释放了 A 对象。

  • 当子线程尝试访问 A 对象时,发现对象已被销毁,因此输出提示信息“A对象已经析构”。

weak_ptr 的详细用法

std::weak_ptr 是一种不增加引用计数的智能指针,它用于观察 std::shared_ptr 所管理的对象。weak_ptr 不拥有对象的所有权,因此不会阻止对象的销毁。这使得 weak_ptr 成为解决循环引用问题的理想选择。

基本用法
  1. 构造 weak_ptr:

    • 可以从 shared_ptr 构造 weak_ptr

    • 也可以从另一个 weak_ptr 构造新的 weak_ptr

    shared_ptr<A> sp(new A());weak_ptr<A> wp1(sp); // 从 shared_ptr 构造weak_ptr<A> wp2(wp1); // 从 weak_ptr 构造
  2. 使用 lock() 方法:

    • lock() 方法尝试将 weak_ptr 转换为 shared_ptr

    • 如果对象仍然存在,则返回一个 shared_ptr 指向该对象。

    • 如果对象已被销毁,则返回一个空的 shared_ptr

    shared_ptr<A> sp = wp.lock();if (sp) {sp->testA(); // 安全访问对象} else {cout << "对象已销毁" << endl;}
  3. 检查是否过期:

    • 使用 expired() 方法检查 weak_ptr 是否指向一个已销毁的对象。

    if (wp.expired()) {cout << "对象已销毁" << endl;}
  4. 重置 weak_ptr:

    • 使用 reset() 方法将 weak_ptr 设置为 nullptr

    wp.reset();
优点
  • 避免循环引用:

    • weak_ptr 不增加引用计数,因此不会导致循环引用问题。

    • 例如,在双向链表中,父节点可以使用 shared_ptr 指向子节点,而子节点可以使用 weak_ptr 指向父节点。

  • 线程安全:

    • weak_ptr 提供了一种安全的方式来访问可能已被销毁的对象。

    • 通过 lock() 方法,可以在多线程环境中安全地访问共享资源。

  • 资源管理:

    • weak_ptr 允许程序在需要时检查对象是否存在,从而避免访问已销毁的对象。

总结

通过上述两个版本的 main 函数示例,我们可以清楚地看到 weak_ptr 在处理可能已销毁的对象时的重要性。使用 lock() 方法尝试将 weak_ptr 提升为 shared_ptr 是一种安全的做法,可以避免访问已销毁的对象,从而防止程序崩溃或产生未定义行为。这种方法不仅适用于单线程环境,在多线程环境中尤为重要,因为它帮助我们有效地管理对象的生命周期,同时保证了线程安全。

http://www.dtcms.com/a/359254.html

相关文章:

  • 广东省省考备考(第九十天8.30)——判断推理(第十节课)
  • Java多线程初阶
  • C++讲解---如何设计一个类
  • 防火墙技术(三):状态检测和会话机制
  • 接口自动化测试框架
  • python pyqt5开发DoIP上位机【自动化测试的逻辑是怎么实现的?】
  • 深度解析Fluss LockUtils类的并发艺术
  • 手写MyBatis第43弹:插件拦截原理与四大可拦截对象详解
  • Agent实战教程:LangGraph结构化输出详解,让智能体返回格式化数据
  • Keil5 MDK_541官网最新版下载、安装
  • offsetof宏的实现
  • 线程池项目代码细节2
  • 互联网医院系统源码解析:如何从零搭建高效的在线问诊平台
  • SNMPv3开发--EngineID安全访问机制
  • 腾讯云的运维笔记——从yum的安装与更新源开始
  • 深入理解 Linux 驱动中的 file_operations:从 C 语言函数指针到类比 C++ 虚函数表
  • centos7中MySQL 5.7.32 到 5.7.44 升级指南:基于官方二进制包的原地替换式升级
  • 有个需求:切换车队身份实现Fragment的Tab隐藏显示(车队不显示奖赏)
  • SNMPv3开发--简单使用
  • 【Linux基础】深入理解Linux环境下的BIOS机制
  • Python - 机器学习:从 “教电脑认东西” 到 “让机器自己学规律”
  • 项目管理和产品管理的区别
  • docker,mysql安装
  • vector的学习和模拟
  • 揭秘表格推理的“思维革命”:RoT模型介绍
  • 【机器学习基础】机器学习中的容量、欠拟合与过拟合:理论基础与实践指南
  • Vue生命周期、工程化开发和脚手架、组件化开发
  • 学习日志41 python
  • 打工人日报#20250830
  • 内网后渗透攻击--跨域攻击