shared_ptr的线程安全性

2023-11-15

shared_ptr的线程安全性

 

shared_ptr的reference count是线程安全的,但是指向的对象不是线程安全的!

 

 

本文链接:https://blog.csdn.net/D_Guco/article/details/80155323

    shared_ptr的出现在某种程度上解放了c++程序员,c++11标准原生的支持了并发编程,在并发编程中shared_ptr的线程安全问题如何保证呢?先撇开shared_ptr对象的线程安全性,先看shared_ptr本身的线程安全问题。

    我们知道,shared_ptr的底层实现原理是引用计数,关于这个计数是否线程安全呢,如果我们把shared_ptr分别传递到不同的线程中,是否会在成引用计数的竞争问题。我们来看shared_ptr引用计数的底层实现。shared_ptr继承了下面的模板类,用它来管理引用计数。其中有两个变量一个表示shared_ptr的引用数,另外一个表示weak_ptr的引用数,我们知道weak_ptr不会增加只能指针的引用数也就是说不持有对象,他的使用必须通过lock方法获取它指向的shared_ptr才能使用。

template<_Lock_policy _Lp = __default_lock_policy>
   class _Sp_counted_base
   : public _Mutex_base<_Lp>
   {
   public:  
     _Sp_counted_base() noexcept
     : _M_use_count(1), _M_weak_count(1) { }
     
     virtual
     ~_Sp_counted_base() noexcept
     { }
 
     //当_M_use_count为0时调用,是个纯虚函数(必须实现),这个函数的作用是释放指针指向的对象所持有的资源,即*this
     virtual void
     _M_dispose() noexcept = 0;
     
     // 当_M_weak_count为0时调用,释放自己本身的资源,即this
     //  _M_weak_count = _M_weak_count + (_M_use_count!= 0),当_M_weak_count和_M_use_count都为0时释放this
     virtual void
     _M_destroy() noexcept
     { delete this; }
     
     virtual void*
     _M_get_deleter(const std::type_info&) noexcept = 0;
 
    //增加一个引用
     void
     _M_add_ref_copy()
     { __gnu_cxx::__atomic_add_dispatch(&_M_use_count, 1); }
 
     void
     _M_add_ref_lock();
 
     bool
     _M_add_ref_lock_nothrow();
 
     void
     _M_release() noexcept
     {
       _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_use_count);
  //首先use_count减去1,并对比减操作之前的值,如果减之前是1,说明减后是0,a1没有任何shared_ptr指针指向它了将销毁对象
if (__gnu_cxx::__exchange_and_add_dispatch(&_M_use_count, -1) == 1)
  {
           _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_use_count);
    _M_dispose();
    //如果destory和dispose存在内存屏障,保证dispose函数的效果在destory函数的调用该线程的可见性
    if (_Mutex_base<_Lp>::_S_need_barriers)
      {
   __atomic_thread_fence (__ATOMIC_ACQ_REL);
      }
 
    //同时对a1的weak_count减去1,也对比减操作之前的值,如果减之前是1,说明减后是0,a1没有weak_ptr指向它了,
    //应该将管理对象销毁,于是调用_M_destroy()销毁了管理对象
           _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_weak_count);
    if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count,
                      -1) == 1)
             {
               _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_weak_count);
        _M_destroy();
             }
  }
     }
 
     void
     _M_weak_add_ref() noexcept
     { __gnu_cxx::__atomic_add_dispatch(&_M_weak_count, 1); }
 
     void
     _M_weak_release() noexcept
     {
       _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_weak_count);
if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count, -1) == 1)
  {
           _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_weak_count);
    if (_Mutex_base<_Lp>::_S_need_barriers)
      {
   __atomic_thread_fence (__ATOMIC_ACQ_REL);
      }
    _M_destroy();
  }
     }
 
    //获取引用计数 
     long
     _M_get_use_count() const noexcept
     {
       return __atomic_load_n(&_M_use_count, __ATOMIC_RELAXED);
     }
 
   private:  
     _Sp_counted_base(_Sp_counted_base const&) = delete;
     _Sp_counted_base& operator=(_Sp_counted_base const&) = delete;
 
     _Atomic_word  _M_use_count;   
     _Atomic_word  _M_weak_count;   
   };

    可能有一些人看过boost中智能指针引用计数的实现,不过好像是通过锁来实现的。这里我们可以看到跟boost的是有所不同的,这里的智能指针的引用计数在手段上使用了atomic原子操作,只要在shared_ptr在拷贝或赋值时增加引用,析构时减少引用就可以了。

    首先源自操作是线程安全的,所有智能指针在多线程下引用计数也是安全的,也就是说智能指针在多线程下传递使用时引用计数是不会有线程安全问题的,但是这能真正的保证shared_ptr指针的线程安全问题吗

     虽然通过原子操作解决了引用计数的计数的线程安全问题, 但是智能指针指向的对象的线程安全问题,智能指针没有做任何的保证。  首先智能指针有两个变量,一个是指向的对象的指针,还有一个就是我们上面看到的引用计数管理对象, 当智能指针发生拷贝的时候,标准哭的实现是县拷贝智能指针,再拷贝引用计数对象(拷贝引用计数对象的时候,会使use_count加一),这两个操作并不是原子的,隐患就出现在这里,引用一下陈硕老师的例子:地址点击打开链接

          

     

       这里陈硕老师说道:“这正是多线程读写同一个shared_ptr必须枷锁的原因”, 为了保证程序的绝对的安全是没错的, 但也不是绝对,上面的情景是特殊场景,这种场景也只是为了说明问题而已,真正开发过程中不一定会用到此场景。其实这个问题的根本还是上面说的智能指针指向的对象的线程安全,shared_ptr没有做任何保证,上面的情景就打破了这一准则,在赋值的过程中,改变了shard_ptr指向的对象的内容,甚至不只是修改了对象这么简单,上面的情景直接把智能指针指向的对象给换了。这中情况不用想肯定会出问题。如果你能保证不会有多个线程同时修改或替换指针指向的对象,不用加锁是完全没有问题的,或者说指针指向的对象本身已经是线程安全(包括多线程下的读写安全和构造析构安全)。总之一句话智能指针指向的对象的线程安全,标准库是没有保证的。 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

shared_ptr的线程安全性 的相关文章

  • 无法在 CUDA 中找到 1 到 100 数字的简单和?

    我正在研究使用 CUDA 的图像处理算法 在我的算法中 我想使用 CUDA 内核找到图像所有像素的总和 所以我在cuda中制作了内核方法 来测量16位灰度图像的所有像素的总和 但我得到了错误的答案 所以我在cuda中编写了一个简单的程序来查
  • 每个元素的 asp.net Web 表单自定义错误消息

    我创建了一个 Web 应用程序 表单 以及后端 SQL 插入和查询 目前我正在显示所有用户错误消息 div style padding 1em div
  • 如何在 C# 中以编程方式将行添加到 DataGrid?

    正如标题所述 我正在尝试使用 C 以编程方式将行添加到 DataGrid 但我似乎无法使其工作 这是我到目前为止所拥有的 I have a DataGrid declared as dg in the XAML foreach string
  • 在 C# 中解析 JS Date.toIsoString

    我需要将 JS 日期存储为 ISO 8601 日期 我目前正在从格式为 2019 06 22T00 00 00 000Z 的表单中获取日期 正如 JS 的 toIsoString 方法所期望的那样 当这个日期传递到我的 API 控制器时 我
  • 注入包含接口的所有已注册实现的 Enumerable

    给出以下接口 public interface IMyProcessor void Process 我希望能够注册多个实现 并让我的 DI 容器将它们的可枚举注入到这样的类中 public class MyProcessorLibrary
  • ASP.NET - Crystal Report Viewer 打印按钮在 ASP.NET 中不起作用

    我正在使用 Visual Studio 2008 但我遇到了水晶报告问题 当我单击打印按钮时 它会将我带到弹出窗口 但未找到页面 弹出的网址是 http localhost aspnet client System Web 2 0 5072
  • 使用 STL 流时如何格式化我自己的对象?

    我想将我自己的对象输出到 STL 流 但具有自定义格式 我想出了这样的东西 但由于我之前从未使用过 locale 和 imbue 所以我不知道这是否有意义以及如何实现 MyFacet 和operator 所以我的问题是 这是否有意义以及如何
  • 如何从 Powerpoint 2010 导出电影?

    如何使用 MS Office PIA 主互操作程序集 或其他方式以编程方式将嵌入视频从 powerpoint 2010 导出到外部文件 在演示文稿中嵌入视频是 Powerpoint 2010 中的一项新功能 我找不到解决方案 PPTX 文件
  • 为什么连续抛出 2 个异常不会生成无法访问的代码警告?

    为什么以下代码行不会创建编译器警告 void Main throw new Exception throw new Exception 据我所知 编译器应该通知您无法到达第二个抛出异常 这显然是一个编译器错误 它是在 C 3 0 中引入的
  • 使用任一默认捕获模式时,这是通过复制捕获还是 (*this) 通过引用捕获?是一样的吗?

    当我看到以下工作时我有点困惑 struct A void g void f g 但后来我发现this https stackoverflow com a 16323119 5825294答案非常详细地解释了它是如何工作的 本质上 它归结为t
  • main.cpp 是必需的吗?

    我试图编译一个程序cmake 我最终删除了我的main cpp文件 我刚刚将其复合到另一个包含我的项目名称的文件中 即 我刚刚将主函数剪切并粘贴到该文件中 问题是我有一个main cpp未发现错误 不确定是否在C 一个名为main cpp是
  • MINIX内部碎片2

    我正在用 C 语言编写一些软件 它递归地列出给定目录中的所有文件 现在我需要计算出内部碎片 我花了很长时间研究这个问题 发现 ext2 上的内部碎片只发生在最后一个块中 我知道理论上你应该能够从索引节点号获得第一个和最后一个块地址 但我不知
  • fgets溢出后如何清除输入缓冲区?

    当输入字符串超出其预定义限制时 我遇到了 fgets 的小问题 以下面的例子为例 for index 0 index lt max index printf Enter the d string index 1 if fgets input
  • 使用未命名命名空间而不是静态命名空间

    我可以假设在未命名命名空间中声明的对象相当于static namespace int x 1 static int x 2 FWIK 在这两种情况下 x将具有静态存储期限和内部链接 声明为的对象的所有规则也是如此static适用于未命名名称
  • 为什么我可以在另一个函数中定义一个函数?

    请参阅下面的代码 我在另一个函数中定义了一个函数 void test1 void void test2 void printf test2 n printf test1 n int main void test1 return 0 这个用法
  • 查找数组中的多个索引

    假设我有一个像这样的数组 string fruits watermelon apple apple kiwi pear banana 是否有一个内置函数可以让我查询 apple 的所有索引 例如 fruits FindAllIndex ap
  • 尝试后终于没有被调用

    由于某种原因 在我的控制台应用程序中 我无法运行我的finally 块 我编写这段代码是为了测试finally块是如何工作的 所以它非常简单 static void Main int i 0 try int j 1 i Generate a
  • 使用通用存储库模式和流畅的 nHibernate

    我目前正在开发一个中型应用程序 它将访问不同站点上的 2 个或更多 SQL 数据库等 我正在考虑使用类似的东西 http mikehadlow blogspot com 2008 03 using irepository pattern w
  • java有类似C#的属性吗? [复制]

    这个问题在这里已经有答案了 C 属性 我的意思是 get 和 set 方法 是一个非常有用的功能 java 也有类似 C 的属性吗 我的意思是我们如何在 java 中实现类似以下 C 代码的内容 public string Name get
  • NHibernate:无状态会话错误消息无法获取代理

    我正在使用 nHibernate 无状态会话来获取对象 更新一个属性并将对象保存回数据库 我不断收到错误消息 无状态会话无法获取代理 我在其他地方有类似的代码 所以我不明白为什么这不起作用 有谁知道问题可能是什么 我正在尝试更新Screen

随机推荐

  • C语言程序——用星号打印图案

    文章目录 前言 一 用星号打印图案 二 程序实例 1 程序代码 2 运行结果 3 结果分析 三 拓展应用 总结 前言 用打印字符来输出星号组成的HELLO 一 用星号打印图案 用星号打印图案 一般利用星号画出具体的模拟输出形式 然后在输出时
  • 【Android】常用对话框大全(一)Android Dialog

    Android的对话框有多少种 Android好看的对话框有很多 如Android material qmui xui kongzue等系列对话框 但博主只打算讲解Android material系列对话框 讲太多没必要 实在想要做成人家那
  • 千万级数据清洗ETL设计方案

    千万级数据清洗项目分析总结 项目简介 一 需求分析 1 前期需求 2 中期需求 3 后期需求 二 技术支持 1 MySQL 2 Redis 三 框架设计 1 流线型代码 2 工厂模式 四 调式工作 1 线上测试 五 问题回顾 1 Mysql
  • scratch python的区别ev3_机器人编程和少儿编程,傻傻分不清—乐高EV3入门感想

    机器人编程和少儿编程的区别 机器人编程和少儿编程不是一个概念 机器人编程是少儿编程的重要组成部分 少儿学习编程大体上是两种方式 1 纯软件 最具代表性的是scratch 是麻省理工学院专门针对小朋友研发的图形化编程语言 无需英文和代码基础
  • win7系统扩展双屏幕时,如何在两个屏幕下都显示任务栏

    扩展屏幕下都显示任务栏 win7系统本身无法设置该功能 目前我是不知道 但可以下载第三方软件来解决该问题 第一步 Dual Monitor Taskbar 下载软件 下载链接 http pan baidu com s 1o61isjw 密码
  • Web 浏览器演变史

    浏览器的演变是由梦想和创新编织而成的 Tim Bernas Lee 在80年代在CERN工作时 提出了HTML技术 用以改善CERN庞大的信息管理需求 Tim 也编写了第一款浏览器 它是基于NeXT提供的interface builder开
  • 【STM32学习笔记】(7)——STM32时钟系统详解

    STM32时钟系统 时钟系统的简介 RCC Reset Clock Control 复位和时钟控制器 时钟是单片机运行的基础 时钟信号推动单片机内各个部分执行相应的指令 时钟系统就是CPU的脉搏 决定cpu速率 像人的心跳一样 只有有了心跳
  • 深度优先搜索(DFS) 广度优先搜索(BFS)

    深度优先搜索算法 Depth First Search DFS是搜索算法的一种 它沿着树的深度遍历树的节点 尽可能深的搜索树的分支 当节点v的所有边都己被探寻过 搜索将回溯到发现节点v的那条边的起始节点 这一过程一直进行到已发现从源节点可达
  • 软件系统产品线特征及构建过程

    根据SEI定义 结合业界的一些研究 软件产品线有如下几个重要特征 1 一个软件产品线应该有一系列的产品成员组成 既产品家族 2 产品家族中的所有产品都服务于一些特定的领域 3 产品家族成员之间在服务功能 产品质量 产品性能 产品应用范围等方
  • Kotlin协程概览

    协程 Coroutines 并不是 Kotlin 提出来的新概念 很多的编程语言都有实现 如 Go Python 等 本文所讲 专指kotlin的协程 在Android 11中 Asynctask已经被废弃了 因为协程可以更简单 直观的实现
  • (管用)Sqlite数据库升级

    调用构造器DBHelper中super里面的方法 增大newVersion的值 就会自动执行onUpgrade 增加数据库字段的sql语句 String upgradeGoods alter table Person add column
  • matlab 里tic toc的用法,Matlab中tic和toc用法

    简单地说 tic和toc是用来记录matlab命令执行的时间 tic用来保存当前时间 而后使用toc来记录程序完成时间 两者往往结合使用 用法如下 tic operations toc 显示时间单位 秒 Tic和toc函数可以计算运行一段时
  • 微信小程序的简单开发案例(记事本)

    微信小程序案例之简单记事本实现 我最近在学习微信小程序 主要熟悉了微信开发者工具的简单使用以及小程序的开发流程 微信小程序的学习要求不高 只要有一些web前端等知识基础即可 1 小程序简介 微信小程序是一种不用下载就能使用的应用 也是一项创
  • vant2-上拉加载、下拉刷新

    可以使用vant组件库实现 上拉刷新 下拉加载功能 vant2开发指南 htt ps youzan github io vant v2 zh CN list 上拉加载 List列表
  • CXF学习笔记三(发布restFul)

    一 概述 JAX RS是Java提供用于开发RESTful Web服务基于注解 annotation 的API JAX RS旨在定义一个统一的规范 使得Java程序员可以使用一套固定的接口来开发REST应用 避免了依赖第三方框架 同时JAX
  • 神经网络算法入门书籍,bp神经网络算法的优点

    请问学bp神经网络哪本书比较好 我研究生3年学的都是这个玩意 你是本科生吧 给你推荐一本书 我和我的同学都觉得这本书非常宝贝 西安电子科技大学出版的一本关于神经网络的书 定价是20元 至少3年前的版本是20元 蓝紫色皮 那本书非常好 不过是
  • Linux ffmpeg编译踩坑

    1 安装freetype字体库 avfilter添加文字水印的依赖项 下载freetype https www freetype org download html 配置 编译 安装 读取版本信息 configure make sudo m
  • Beego框架基本使用实践教程

    项目介绍 一款 Go 语言基于Beego Layui MySQL等框架精心打造的一款模块化 高性能 企业级的敏捷开发框架 本着简化开发 提升开发效率的初衷触发 框架自研了一套个性化的组件 实现了可插拔的组件式开发方式 单图上传 多图上传 下
  • 计算机专业毕业论文指导记录详细,毕业论文指导记录怎么写 了解一下

    1 指导记录总共6次 请各位同学按照法学院网站上公布的毕业论文写作进度确定每次指导记录的时间 同时根据自己的论文选题和实际情况 可稍作调整 2 第一次 根据学院的统一部署 布置学生毕业论文写作的总体要求与进度 要求学生严格按照学院的部署 积
  • shared_ptr的线程安全性

    shared ptr的线程安全性 shared ptr的reference count是线程安全的 但是指向的对象不是线程安全的 本文链接 https blog csdn net D Guco article details 8015532