<<Linux多线程服务端编程>>学习之栏1————线程安全的对象生命期管理

news2024/11/23 15:46:07

线程安全的对象生命期管理

此章节开头的前两句话,把我点醒,原来思考功力可以这么深厚!如下:

  • 第一句话: 编写线程安全的类不是难事, 用同步原语保护内部状态即可;

  • 第二句话: 但是对象的生与死不能由对象自身拥有的mutex(互斥器) 来保护。

上述可以作为本章节的一个开篇词,值得每一位C++多线程开发者的回味!

1——析构遇到多线程
1.1——定义线程安全
1.2——如何保证对象构造时的线程安全?
1.3——Mutex真的可以保证对象析构时的安全?
1.4——Observer观察者模式的常规实现和线程安全性分析
1.5——智能指针简述
1.6——智能指针应用到Observer上

[1].析构遇到多线程

思考如下几个问题?

前提: 一个对象被多个线程同时能够观测到;

  • 问题1:即将析构一个对象时, 如何知道是否有别的线程正在执行该对象的成员函数?
  • 问题2:如何保证在执行成员函数期间,对象不会在另一个线程被析构?
  • 问题3:在调用某个对象的成员函数之前, 如何得知这个对象还活着? 它的析构函数会不会碰巧执行到一半?

本章的意义: 利用shared_ptr解决上述模糊不清的资源竞争问题,减轻多线程编程的负担!

[1.1].定义线程安全

文献依据: Brian Goetz. Java Concurrency in Practice. Addison-Wesley, 2006

定义: 一个线程安全的类需要满足以下三个条件!

  • 多个线程同时访问时, 其表现出正确的行为
  • 无论操作系统如何调度这些线程, 无论这些线程的执行顺序如何交织, 其表现出正确的行为
  • 调用端代码无须额外的同步或其他协调动作

附加: 依据这个定义, C++标准库里的大多数class都不是线程安全的, 包括std:: string、 std::vector、 std::map等

[1.2].如何保证对象构造时的线程安全?

一句话: 构造期间不要泄露this指针!

展开说:

  • 不要在构造函数中注册任何回调
  • 不要在构造函数中把this传给跨线程的对象
  • 即便在构造函数的最后一行也不行

目的: 构造未完成,将this暴露在外,就类似衣服没穿,就出门了,必定发生难以预料的事情。
难易程度: 很容易保证。

[1.3].Mutex真的可以保证对象析构时的安全?

考虑如下的类Foo的代码:

主干: 一个析构函数、一个update函数

在这里插入图片描述

执行背景: 两个线程A和B,一个类Foo。A和B共同访问一个对象x,当A线程对x析构时,B线程对foo调用update

在这里插入图片描述

执行结果: 当线程A执行析构,互斥体已经被析构后;线程B又加锁。这种行为肯定是未定义的,其实这就跟访问已经释放的内存是一个道理!最好的情况也就是永远阻塞,很可能直接core dump!

结论: 作为数据成员的互斥锁并不能保护析构!

[1.4].Observer观察者模式的常规实现和线程安全性分析

代码如下:

在这里插入图片描述
在这里插入图片描述

简单讲解: Observer是观察者,Observable是可被观察的物体。可被观察的物体通过register_成员函数,能够添加多个观察者。这样物体每次变化的时候,只需要调用nofifyObservers即可对所有观察者进行通知,实现一对多的消息传递!

多线程场景分析: Observable类的nofifyObservers()在遍历观察者的时候,17行这里,它如何得知x是否已经消亡了呢?同理,Observer在析构中,32行,它如何得知subject_是否存活呢?这些都是线程安全的观察者模式的实现难点!

关键点猜测: 似乎线程安全的关键点在于,如何通过指针能够判断对象是否还活着?类似这种的功能,仔细想象这不就是类似于代理的功能么,原生指针肯定没办法,所以需要一个wrapper类,也就是智能指针,它包括着原始指针!

[1.5].智能指针简述

智能指针解决的最典型问题:

1、空悬指针
在这里插入图片描述
如上图,加入P1和P2的原始指针都指向Object,如果P1将指针置空,但是P2并不知道,再使用就出错了!

如何解决的?

思路一: 引入间接访问层,让P1和P2指向的对象永久有效,这个对象持有Object指针,如下图所示:
在这里插入图片描述
当Object被销毁,proxy对象仍然存在,其内存指针值为0,可以通过proxy的接口进行判断对象是否存活!如下:
在这里插入图片描述

存在的问题: 线程安全的释放Object并不容易,竞争仍然存在;比如:当P2去看proxy内容不为空,数据存在。这时去调用Object的成员函数,但是在调用过程中对象被P1销毁了,这时候又出现问题!

思路二: 为了安全释放proxy,引入引用计数,Object对象的生命周期完全由proxy掌控,如下图:

(1)一开始,两个引用,计数为2
在这里插入图片描述
(2)sp1析构了,计数-1
在这里插入图片描述
(3)sp2析构了,计数-1,这时为0,Object自动由proxy释放内存
在这里插入图片描述

shared_ptr简述: 它被C++11标准库引入,是一个类模板,利用引入计数进行自动化的资源管理,引用计数为0,对象销毁,强引用,控制对象生命周期!

weak_ptr简述: 同理,也是C++11引入,但是它不直接增加对象引用计数,是从shared_ptr偷来的资源使用,是弱引用,不控制对象生命周期,通过线程安全的接口lock()进行提升为shared_ptr,从而判断对象是否已经释放!

shared_ptr的线程安全性: 本身不是100%线程安全,它的引用计数是安全且无所,但对象的读写不是,因此不是线程安全的。如何评价呢?三句话!如下:

  • 一个shared_ptr对象实体可被多个线程同时读取
  • 两个shared_ptr对象实体可以被两个线程同时写入, “析构”算写操作;
  • 如果要从多个线程读写同一个shared_ptr对象, 那么需要加锁。

例子如下:
在这里插入图片描述
分析: 单纯的针对同一个智能指针对象进行读写的时候,需要加锁。然后创建局部智能指针对象后,这样针对局部的智能指针的读写操作都不需要加锁!

智能指针作为函数参数——最常见使用方式: 使用const reference方式传递智能指针,只需要在最外层由一个local shared_ptr实体,然后调用函数都通过这个实体的const引用即可!如下图:
在这里插入图片描述在这里插入图片描述

综上: shared_ptr的线程安全级别和STL容器一致,并不是线程安全的!多线程场景下需要注意再注意,同时在作为参数传递时,也需要小心再小心!

[1.6].智能指针应用到Observer上

针对Observable的改造如下图所示:

在这里插入图片描述
评价: 这里虽然解决了Observer模拟的线程安全问题,但是仍有许多问题,疑点重重。如下:

疑点:

  • 侵入性。强制要求Observer必须以shared_ptr来管理;
  • 不是完全线程安全。Observer的析构函数会调用subject_->unregister(this), 万一subject_已经不复存在了呢? 为了解决它, 又要求Observable本身是用shared_ptr管理的, 并且subject_多半是个weak_ptr;
  • 锁争用。Observable的三个成员函数都用了互斥器来同步,会造成register_()和unregister()等待notifyObservers(), 而
    后者的执行时间是无上限的;
  • 死锁。万一L62的update()虚函数中调用了(un)register呢? 如果mutex_是不可重入的, 那么会死锁!

结尾: 我是航行的小土豆,喜欢我的程序猿朋友们,欢迎点赞+关注哦!希望大家多多支持我哦!有相关不懂问题,可以留言一起探讨哦!

如有引用或转载记得标注哦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/622752.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【论文阅读】ControlNet

简介 目标:加入额外的条件(例如边缘图像,深度图像)控制生成的图像 现有挑战 特定领域上的数据较少,而预训练模型很大,很容易出现过拟合的情况。在资源有限的情况下,只能选择pretrain- finetun…

传统工业制造企业如何实现数字化转型?

传统工业制造企业如何实现数字化转型,以数字驱动、实现高价值管理? 传统企业实现数字化转型是一条很漫长但不得不走的道路,看到这个问题下有很多专业人士对传统企业如何做数字化转型都提出了专业的见解,所以这篇就以传统制造业为…

用ChatGPT来写高考作文,看看效果!

又是一年高考日,今天高考作文题目一出来,很多人第一时间就用AI进行写作,我这边也用gpt3和4分别生成了一篇文章,没有给他投喂范文,把要求和题目的prompt给它,让它分析和写作,来看看效果吧。 GPT4…

学习态度记录JRebel本地验证

学习态度记录JRebel本地验证 网上有两种方式。 1、直接使用其他同学提供好的远程服务器验证地址(可自行搜索) 2、设置本地反向代理,激活JRebel ps:我的IDEA升级到2023.1.2后无法使用第一种方式了。搜了半天网上都是基于windows环境的教程解说&#xff0…

html 原生js手写树 仿照antd 样式

效果如图 <!doctype html> <html><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-width, initial-scale1.0"…

百度Apollo视频学习笔记

APOLLO视频学习笔记 一、总览 无人驾驶车的运作方式 五个核心部件&#xff1a; 计算机视觉&#xff1a;弄清楚周围的世界是怎样的传感器融合&#xff1a; 合并来自其他传感器的数据&#xff0c;如激光和雷达&#xff0c;更加深入了了解我们周围的环境定位&#xff1a;精确地…

一级建造师执业资格考试--工程法规--速学36记--联想法

第一记&#xff1a;法的效力层级 第二记&#xff1a;法人的分类 【速记方法】口诀&#xff1a;赚钱营利和特别 关键词&#xff1a;营利、特别 【速记内容】 1、营利法人:以取得利润并分配给股东等出资人为目的成立的法人,为营利法人; 如有限责任公司、股份有限公司。经依法登记…

什么牌子电容笔性价比高?iPad触屏笔推荐

电容笔已经成为日常生活中不可或缺的一部分。它可以用于书写&#xff0c;绘画&#xff0c;甚至玩游戏。使用电容笔可以代替传统无纸化书写&#xff0c;提高工作效率。市面上有许多不同价格的电容笔品牌可供选择。本文将介绍四款性价比高的平替电容笔&#xff0c;有需要入手的小…

常用的三种拖拽方法(内置方法 + 接口 + Event Trigger组件)

前言 在Unity中实现拖拽的方法有多种&#xff0c;以下是几种常见的方法和它们的优缺点&#xff1a; Input.GetMouseButtonDown Input.GetMouseButtonDown 方法可以监测用户鼠标按键的点击事件&#xff0c;通过检测鼠标按钮的状态来实现拖拽效果。用户通过鼠标进行拖拽操作。…

Web基本概念

一、前言 World Wide Web的简称&#xff0c;是一个由许多互相链接的超文本组成的系统&#xff0c;通过互联网访问 &#xff08;为用户提供信息&#xff09; 静态网页 仅适用于不能经常更改内容的网页&#xff1b; 动态网页 网络编程技术创建的页面&#xff1b;通过在传统的静态…

MongoDB安装与使用

目录 一、MongoDB介绍与安装 什么是MongoDB 为什么要用MongoDB MongoDB下载 MongoDB安装完成 二、MongoDB Compass MongoDB Compass使用 三、使用mongoose连接数据库 使用MongoDB Compass 创建数据库 使用mongoose 连接数据库 每日一课&#xff1a;MongoDB 常用命令…

Vue项目打包dist目录介绍

如下 CSS目录&#xff1a; ① .css文件是项目要用到的css文件,当你做webpack打包的时候&#xff0c;会把所有的css样式打包到这里 ② .css.map文件是一个Source map文件&#xff0c;Source map就是一个信息文件&#xff0c;里面储存着位置信息。也就是说&#xff0c;转换后的代…

ChatGPT扩展系列之使用pandora本地搭建ChatGPT

ChatGPT扩展系列之使用pandora本地搭建ChatGPT 1. 为什么要本地搭建 主要解决使用上的几个痛点,我们可以看一下下面就是我们最常遇到的几个问题,这里我们重点提一下就是我们本地搭建好了之后,我们获取Access Token,这个Token的有效期长达14天,也就是这14天中,我们都不需…

【沐风老师】3DMAX径向对称插件使用方法应解

3DMAX径向对称插件使用教程 3DMAX径向对称插件&#xff0c;允许你对径向结构建模并查看最终结果。它的功能类似于3dMax自带的“对称”修改器&#xff0c;但它可以在三个轴的任意角度径向对象&#xff0c;这可以创造出很多我们意想不到的建模艺术效果&#xff0c;也可以理解它是…

【前端 - HTML】第 6 课 - 表单标签

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快 &#xff01; 时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、缘起 2、表单标签 2.1、input 标签基本使用 2.2、input 标签占位文本 2.3、单选框 radio 2.4、上传文件 2.5、多选框 …

Vue.js中的动态组件和异步组件

Vue.js中的动态组件和异步组件 在Vue.js中&#xff0c;动态组件和异步组件是两个常用的技术&#xff0c;用于处理动态加载和渲染组件的需求。虽然它们都可以实现动态加载和渲染组件的功能&#xff0c;但它们的实现方式和使用方法有所不同。本文将详细介绍Vue.js中的动态组件和…

短视频账号矩阵系统技术开发难度之.框架底层逻辑

申请流程&#xff1a;注册官方开放平台账号----申请服务商-----关联应用----申请权限-----等待审核通过 接入开发------开发功能列表&#xff1a; 数据归纳箱&#xff08;账号数据对比概览内含视频总数、播放总数、点赞总数、分享总数、粉丝总数数据统计概览统计&#xff09;…

CMU15-445 2022 Fall 通关记录 —— Project 0

Project 0 Project #0 - C Primer | CMU 15-445/645 :: Intro to Database Systems (Fall 2022) — 项目 #0 - C 入门 | CMU 15-445/645 :: 数据库系统简介&#xff08;2022 年秋季&#xff09; 前期准备 为完成该项目做的一些准备&#xff1a; 创建个人项目FarewellYi/BusT…

【半监督医学图像分割 2023 CVPR】BCP

【半监督医学图像分割 2023 CVPR】BCP 论文题目&#xff1a;Bidirectional Copy-Paste for Semi-Supervised Medical Image Segmentation 中文题目&#xff1a;双向复制粘贴半监督医学图像分割 论文链接&#xff1a;https://arxiv.org/abs/2305.00673 论文代码&#xff1a;http…

EXCEL和VBA里的通配符和转义符

1 EXCEL里的通配符 1.1 EXCEL里常见通配符 通配符必须是英文半角的&#xff0c;中文输入下的不行&#xff01;* 可代表任意数量的字符&#xff1f; 可代表任一个的字符 1.2 使用举例 EXCEL的查找框&#xff0c;也可以使用 通配符只有部分内置函数可…