并发编程线程安全性之可见性有序性

可见性

可见性: 就是说一个线程对共享变量的修改,另一个线程能够立刻看到

通俗点说,就是两个线程共享一个变量,无论哪一个线程修改了这个变量,另外一个线程都能够立刻看到上一个线程对这个变量的修改

产生线程安全问题的原因

计算机是利用CPU进行数据运算的,但是CPU只能对内存中的数据进行运算,对于磁盘中的数据,必须要先读取到内存,CPU才能进行运算。cpu,内存,磁盘都会影响计算机的处理性能,同时这三者之间有个核心的矛盾点,就是三者在处理速度上的差异。CPU的计算速度是非常快的,其次是内存、最后是IO设备(比如磁盘),也就是说CPU的计算速度是远远高于内存以及磁盘设备的I/O速度的。

为了平衡这三者之间的速度差异,最大化的利用CPU。所以在硬件层面、操作系统层面、编译器层面做出了很多的优化

  • CPU增加了高速缓存
  • 操作系统增加了进程、线程。通过CPU的时间片切换最大化的提升CPU的使用率
  • 编译器的指令优化,更合理的去利用好CPU的高速缓存

每一种优化,都会带来相应的问题,而这些问题是导致线程安全性问题的根源。

CPU高速缓存

解决的问题:CPU高速缓存的出现主要是为了解决CPU运算速度和内存读写速度不匹配的矛盾,因为CPU运算速度要比内存读写速度快很多,这样会使CPU花费很长时间等待数据到来或把数据写入内存。

打开任务管理器  可以看到CPU的3个缓存

思考: 有了高速缓存后会带来什么问题?

分析:CPU0和CPU1并行处理同时从内存中加载数据到缓存中,此时CPU0改变了这个值,会再同步到内存中,CPU1什么时候再读到这个最新的值是不确定的,因此会存在缓存一致性问题,那么应该怎么解决呢?

在解决这个问题之前,先来了解以下伪共享和缓存行填充

伪共享和缓存行填充

缓存行:CPU的缓存是由多个缓存行组成的,最小交互单元

伪共享:

当有两个线程读取同一缓存行的不同值时,就会去竞争这同一缓存行,这就是伪共享问题。

思考:怎么解决?对齐填充

缓存一致性问题和缓存一致性协议

  • 总线锁
  • 缓存锁
  • 缓存一致性协议(MESI  MOSI) MESI表示缓存的四种状态 修改 失效 独占 共享

如果是S状态 修改时 要先把其它缓存设置为失效 失效状态从内存中读

snoopy协议会监听总线上的事件

假设a=1这个数据要进行修改,刚读进来是共享状态,当需要修改时,会通过总线发送一个指令,让其它缓存失效,然后修改完后,其它缓存从内存中读数据,就解决了缓存一致性的问题。

CPU指令重排序 

cpu层面是如何导致指令重排序的? 如下图

CPU0要写入一个数据时,其它CPU在失效的时候,CPU0是处于阻塞状态的,要等到所有的缓存行失效后,再做写入操作,保证缓存一致性。 异步的思想就是会把数据加载到storebuffer中,继续执行其它的指令,等到其它的缓存行都失效后,再把数据从store buffe加载出来到缓存行

这段代码是如何存在指令重排序的

当CPU从内存中加载a=1时,会先加载到store buffer中,会让其它的缓存行失效,然后当前缓存行中的a=0(缓存一致性)变更为独占状态。然后CPU0从内存中读取b=0放到缓存行中,然后执行b=A+1此时计算出来的结果为1,然后此时其它CPU全部失效了,这时候CPU0再从store buffer中把a=1加载到缓存行,就造成了指令的重排序。

Store Forwarding

如何优化指令重排序?store forwarding就是CPU0从缓存行中读取数据,那么按照上面的例子,b=a+1加载到缓存行的数据,就是a=1了

分析: 假设a=0 存在于cpu1的缓存行中,b=0存在于cpu0的缓存行中 且都为独占状态。cpu0执行的两个代码为写操作 更新操作 CPU1只进行读。当CPU读取b的值时,就向cpu0发起请求,此时cpu0将b变更后设为共享状态。紧接着 a就在cpu1中,因此断言失败,此时 CPU1收到了CPU0的读请求,于是才让a=0缓存行失效,然后加载到cpu0中并变为独占状态后 在进行修改 。

引出的问题怎么办?

Invalid Queue

这个图就相当于 让其它缓存行失效的过程, 由同步 变为了 异步 直接丢到了invalidate中进行处理 提高效率。

CPU性能的博弈之路:

内存屏障

CPU层面不知道  什么时候不允许优化  什么时候优化

  • 读屏障
  • 写屏障
  • 全屏障

Lock: 缓存锁总线锁

在不同的CPU架构中,实现内存屏障的指令不同

JMM模型

Happens-Before模型(告诉你哪些场景不会存在指令重排序问题)

并不是所有的程序指令都会存在可见性或者指令重排序问题,其实质性描述的是可见性规则

规则1:程序顺序型规则(as-if-serial)

不管程序如何重排序,单线程的执行结果一定不会发生变化

规则2:传递性规则

如果A happens before B     B happens before C   那么A happens before C成立

规则3:volatile变量规则

规则4:监视器锁规则

规则5:Start规则

规则6:join规则

总结

可见性导致的原因 1.CPU的高速缓存 2.指令重排序

MESI协议保证缓存的一致性

指令重排序在不同的架构中有着不同的内存屏障指令来解决

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

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

相关文章

桶装水配送小程序有什么功能 怎么制作

桶装水配送小程序是一种方便快捷的订水服务工具,可以帮助用户轻松订购桶装水,并实时跟踪订单配送情况。下面具体介绍送水小程序的功能。 1. 客户订水功能:用户可以通过小程序轻松选择需要的桶装水数量和配送时间,填写联系信息和地…

LeetCode 2960.统计已测试设备

给你一个长度为 n 、下标从 0 开始的整数数组 batteryPercentages ,表示 n 个设备的电池百分比。 你的任务是按照顺序测试每个设备 i,执行以下测试操作: 如果 batteryPercentages[i] 大于 0: 增加 已测试设备的计数。 将下标在 …

leet hot 100-7 无重复字符的最长子串

无重复字符的最长子串 原题链接思路代码 原题链接 leet hot 100-7 3. 无重复字符的最长子串 思路 声明一个无序map容器,容器里面记录着窗口里面字符的数量 ,如果字符的数量大于1 遍历到s[i]的时候 数量1 此时如果map里面相关的数量大于1 说明窗口里面…

ocr识别tesseract.js本地复现

来源: https://github.com/naptha/tesseract.js chatgpt今天帮倒忙,一直给一些旧的东西,代码就老报错,最后还是我出面看看log和err调了一下,还的是我啊 复现效果 这个挺好复现的,用的英文模式比中文识别…

阿里云服务器ECS u1实例性能如何?199元一年

阿里云服务器ECS u1实例,2核4G,5M固定带宽,80G ESSD Entry盘优惠价格199元一年,性能很不错,CPU采用Intel Xeon Platinum可扩展处理器,购买限制条件为企业客户专享,实名认证信息是企业用户即可&a…

如何本地构建一个 dpdk l2fwd snap 包?

目标 基于 dpdk-19.11 l2fwd 程序及其依赖库本地构建一个 snap 包&#xff0c;能够在 ubuntu 20.04 桌面环境中安装并测试运行。 编写 snap 包 yaml 描述文件 yaml 文件&#xff1a; name: test # you probably want to snapcraft register <name> base: core22 # th…

常用芯片学习——YC688语音芯片

YC688 广州语创公司语音芯片 使用说明 YC688是一款工业级的MP3语音芯片 &#xff0c;完美的集成了MP3、WAV的硬解码。支持SPI-Flash、TF卡、U盘三种存储设备。可通过电脑直接更新SPI-Flash的内容&#xff0c;无需上位机软件。通过简单的串口指令即可完成三种存储设备的音频插…

C/C++文件操作

一、文本文件操作 1、写文件操作 代码 #include<fstream> #include<iostream>int main() {ofstream outfile("Student.txt", ios::out);if (!outfile) {cout << "文件写入失败" << endl;exit(0); //程序终止}cout << &qu…

R绘图 | 单列数据的分布图,对A变量分bin求B变量的平均值

问题1&#xff1a;单个向量的 density 分布图&#xff1f; (1) 模拟数据 set.seed(202402) datdiamonds[sample(nrow(diamonds), 1000),]> head(dat) # A tibble: 6 10carat cut color clarity depth table price x y z<dbl> <ord> &l…

物资管理新篇章:Java+SpringBoot实战

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

微信小程序本地开发

微信小程序本地开发时不需要在小程序后台配置服务器域名直接在小程序项目中填写后端在本机的IP地址和端口号 如图&#xff08;第一步&#xff09; 填写地址后发现报错&#xff0c;url不是合法域名&#xff0c;则在详情设置不校验合法域名 如图&#xff08;第二歩&#xff09;…

什么时候用ref和reactive

在Vue 3中&#xff0c;ref和reactive都是用于创建响应式数据的工具&#xff0c;但它们的使用场景有所不同。 使用ref的情况&#xff1a; 基本数据类型&#xff1a;当你需要响应式地处理基本数据类型&#xff08;如数字、字符串、布尔值&#xff09;时&#xff0c;应该使用ref…

算法训练营day35, 复习二叉树3

package main type TreeNode struct { Val int Left *TreeNode Right *TreeNode } // 2476. 二叉搜索树最近节点查询 //思路先用中序遍历 左中右获取有序数组&#xff0c;在用二分法寻找最大&#xff0c;最小值 func closestNodes(root *TreeNode, queries []int) [][]i…

Flink代码单词统计 ---批处理

flatMap&#xff1a;一对多转换操作&#xff0c;输入句子&#xff0c;输出分词后的每个词groupBy&#xff1a;按Key分组&#xff0c;0代表选择第1列作为Keysum&#xff1a;求和&#xff0c;1代表按照第2列进行累加print&#xff1a;打印最终结果 1.WordCount代码编写 需求&am…

统一管理项目的线程池

一、问题描述 频繁的创建、销毁线程和线程池,会给系统带来额外的开销,也有可能导致系统内线程数上限不可控。例如以下代码,每次发送邮件都会创建一个新的线程池,并且业务结束之后线程池也未随之销毁。 public static boolean sendMail(MailInfo mailInfo, MailServerInfo…

如何从零实现一个词云效果

词云是一种文本数据的可视化形式&#xff0c;它富有表现力&#xff0c;通过大小不一&#xff0c;五颜六色&#xff0c;随机紧挨在一起的文本形式&#xff0c;可以在众多文本中直观地突出出现频率较高的关键词&#xff0c;给予视觉上的突出&#xff0c;从而过滤掉大量的文本信息…

Segment Routing IPv6介绍

定义 SRv6&#xff08;Segment Routing IPv6&#xff0c;基于IPv6转发平面的段路由&#xff09;是基于源路由理念而设计的在网络上转发IPv6数据包的一种协议。SRv6通过在IPv6报文中插入一个路由扩展头SRH&#xff08;Segment Routing Header&#xff09;&#xff0c;在SRH中压…

matlab 线性四分之一车体模型

1、内容简介 略 57-可以交流、咨询、答疑 路面采用公式积分来获得&#xff0c;计算了车体位移、非悬架位移、动载荷等参数 2、内容说明 略 3、仿真分析 略 线性四分之一车体模型_哔哩哔哩_bilibili 4、参考论文 略

Mysql查询或导出结果添加序号字段方法

在MySQL中查询数据或者导出Excel时&#xff0c;通常都需要给结果集中的每一行来添加一个序号&#xff0c;方便给查询出或者导出的数据进行排序并且方便查看和处理数据&#xff0c;常见的方法是通过定义用户变量&#xff0c;在MySQL中直接给查询结果集添加序号。 通过定义用户变…

十一、Qt自定义Widget组件、静态库与动态库

一、自定义Widget组件 1、自定义Widget组件 使用步骤采用提升法&#xff08;promotion&#xff09;重新定义paintEvent事件 2、实现程序 &#xff08;1&#xff09;创建项目&#xff0c;基于QWidget &#xff08;2&#xff09;添加类&#xff0c;为Widget组件提升类 #inclu…