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

理解计算机系统_线程(九):线程安全问题

前言
       

        以<深入理解计算机系统>(以下称“本书”)内容为基础,对程序的整个过程进行梳理。本书内容对整个计算机系统做了系统性导引,每部分内容都是单独的一门课.学习深度根据自己需要来定

引入

        接续理解计算机系统_线程(八):并行-CSDN博客,内容包括12.7.1~12.7.3

概念浅析

        本书P716~P719讲了线程安全问题,笔者个人感觉不太好理解,从使用的角度对几个概念梳理.

        1.同步共享是紧密相连的.

                凡是说同步问题,必然牵涉到共享数据.再直白一点,函数体内部有全局变量.

        2.线程不安全函数=同步+无锁

        3.线程安全函数=同步+有锁

        4.可重入函数=不同步

        如图

同步再分析

        从函数,使用的角度再分析同步

        1>从函数角度来看,同步属于"程序副作用"(以前提过).同步可看作是多线程下的程序副作用.

        2>站在使用者的角度,同步应尽量避免(本书P716第1段:同步从根本上来说是很难的问题,他引出了在普通顺序程序中不会出现的问题.---黑体字是原话),在前一贴并行的讨论中,也是选择了绕开同步.

        当同步不可避免时,需要考虑线程安全(加锁)

线程安全

        概念:当且仅当被多个并发线程反复地调用时,他会一直产生正确的结果.这就是线程安全函数

        笔者用了个简单的视角来看待线程安全的函数:"同步加有锁"---当一个函数的定义中有全局变量,并且在访问全局变量时加上了锁(互斥锁),那他就是线程安全的函数.显然这种看法并不严谨(两点限制:一是目前除了锁以外没有保证线程安全的写法;二是笔者对于线程安全的知识仅来自于本书),除去以上两点限制,笔者的看法是可用的.

        本书P716开始介绍了四种线程不安全函数类,都可以概括在内,简单如下理解:

        第1类:不保护共享变量的函数

                线程函数中使用了共享变量,没加锁.---基础情况

        第2类:保持跨越多个调用的状态的函数

                本书举了一个例子,如图

                线程函数调用的函数中,使用了共享变量.---和1的情况差不多,只不过相当于包装了一层

                即使不考虑线程安全,这样写代码(程序副作用)也是比较"忌讳"的.原因是全局变量随时处于被更改的状态,不知道代码当前状态下的全局变量是哪一个了.解决办法在P718可重入性里讲了--传入指针,随时保持全局变量的更新.

                本书P716:使得像rand这样的函数线程安全的唯一方式是重写他,使得他不再使用任何static数据,而是依靠调用者在参数中传递状态信息.这样做的缺点是,程序员现在还要被迫修改调用程序中的代码.---黑体字是原话

                ---解读:笔者认为书中做法和线程安全无关,他说的意思是把程序副作用变为可重入函数,代码如下:

//函数定义
unsigned rand(int replace_data){replace_data=replace_data*1103515245+12543;return (unsigned)(replace_data>>16)%32768;
}unsigned srand(unsigned new_seed){return new_seed;
}//以下为调用
int random=srand(rand(1));

                和全局变量脱离关系(用的是局部变量),和线程安全更加无关了.

        第3类:返回指向静态变量的指针的函数.

                某些函数,例如ctime和gethostbyname,将计算结果放在一个static变量中,然后返回一个指向这个变量的指针.有两种方法来处理这类线程不安全函数.一种选择重写函数.一种选择就是使用加锁-复制技术.----黑体字是原话

                ---解读:实际上这是一种特别的例子.返回值既return了,又从形参返回.解决办法还是加锁.

        第4类,调用线程不安全函数的函数.        

        这4类线程不安全的函数,解决办法都是加锁.举的例子中有些比较特殊,不用过度解读. 

可重入性     

        本书原话:其特点在于他们具有这样一种属性:当他们被多个线程调用时,不会引用任何共享数据.         

        ---解读:线程安全或不安全,特点是函数体内部使用了共享数据,可重入函数没有共享数据,所以和线程安全无关.

显式可重入和隐式可重入

        显式可重入:如果所有的函数参数都是传值传递(即没有指针),并且所有的数据引用都是本地自动栈变量(即没有引用静态或全局变量),那么函数就是显式可重入的.

        隐式可重入:如果调用线程小心地传递指向非共享数据的指针,那么他是可重入的.

        特别注意:如果传递了指向全局变量的指针,他名义上是可重入,但又成为了线程不安全的函数.这一点书上写得很隐晦,但分析可以得出结果:如果线程函数中调用rand_r,而rand_r传入了全局变量的指针给nextp,不加锁仍然会出现同步问题.---如果笔者的认识是错的,那么更简单.

在线程化的程序中使用已存在的库函数

        如图

        为保险起见,给线程安全版本传入全局变量的副本(copy),而不是直接传全局变量

小结

        线程安全的理解

http://www.lryc.cn/news/2387444.html

相关文章:

  • vue3基本类型和对象类型的响应式数据
  • 3.8.4 利用RDD实现分组排行榜
  • python web flask专题-Flask入门指南:从安装到核心功能详解
  • C语言中的“类框架”工具
  • 【HW系列】—web组件漏洞(Strtus2和Apache Log4j2)
  • 第六十八篇 从“超市收银系统崩溃”看JVM性能监控与故障定位实战
  • Debian 11 之使用hostapd与dnsmasq进行AP设置
  • 有铜半孔的设计规范与材料创新
  • 机器学习知识体系:从“找规律”到“做决策”的全过程解析
  • STM32之FreeRTOS移植(重点)
  • 做好测试用例设计工作的关键是什么?
  • R语言科研编程-标准偏差柱状图
  • 未来教育考试答题软件4.0【自用链接备份】
  • OpenGL Chan视频学习-11 Uniforms in OpenGL
  • Flink系列文章列表
  • GitLab 从 17.10 到 18.0.1 的升级指南
  • 产业集群间的专利合作关系
  • PyQt学习系列02-模型-视图架构与数据管理
  • redis主从复制架构安装与部署
  • Kotlin 中 Lambda 表达式的语法结构及简化推导
  • YOLOv2 深度解析:目标检测领域的进阶之路
  • KT6368A通过蓝牙芯片获取手机时间详细说明,对应串口指令举例
  • 计算机网络实验课(二)——抓取网络数据包,并实现根据条件过滤抓取的以太网帧,分析帧结构
  • 自动生成提示技术突破:AUTOPROMPT重塑语言模型应用
  • 78. Subsets和90. Subsets II
  • VSCode 插件 GitLens 破解方法
  • linux 通过命令将 MinIO 桶的权限设置为 Custom(自定义策略)
  • 模型评价指标介绍
  • ElasticSearch整合SpringBoot
  • ArcGIS Pro 3.4 二次开发 - 知识图谱