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

2024-02-26 23:52

本文主要是介绍并发编程线程安全性之可见性有序性,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

可见性

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

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

产生线程安全问题的原因

计算机是利用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.chinasem.cn/article/750569

相关文章

MySQL的JDBC编程详解

《MySQL的JDBC编程详解》:本文主要介绍MySQL的JDBC编程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、前置知识1. 引入依赖2. 认识 url二、JDBC 操作流程1. JDBC 的写操作2. JDBC 的读操作总结前言本文介绍了mysq

Java中如何正确的停掉线程

《Java中如何正确的停掉线程》Java通过interrupt()通知线程停止而非强制,确保线程自主处理中断,避免数据损坏,线程池的shutdown()等待任务完成,shutdownNow()强制中断... 目录为什么不强制停止为什么 Java 不提供强制停止线程的能力呢?如何用interrupt停止线程s

Web服务器-Nginx-高并发问题

《Web服务器-Nginx-高并发问题》Nginx通过事件驱动、I/O多路复用和异步非阻塞技术高效处理高并发,结合动静分离和限流策略,提升性能与稳定性... 目录前言一、架构1. 原生多进程架构2. 事件驱动模型3. IO多路复用4. 异步非阻塞 I/O5. Nginx高并发配置实战二、动静分离1. 职责2

python 线程池顺序执行的方法实现

《python线程池顺序执行的方法实现》在Python中,线程池默认是并发执行任务的,但若需要实现任务的顺序执行,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋... 目录方案一:强制单线程(伪顺序执行)方案二:按提交顺序获取结果方案三:任务间依赖控制方案四:队列顺序消

Python异步编程之await与asyncio基本用法详解

《Python异步编程之await与asyncio基本用法详解》在Python中,await和asyncio是异步编程的核心工具,用于高效处理I/O密集型任务(如网络请求、文件读写、数据库操作等),接... 目录一、核心概念二、使用场景三、基本用法1. 定义协程2. 运行协程3. 并发执行多个任务四、关键

AOP编程的基本概念与idea编辑器的配合体验过程

《AOP编程的基本概念与idea编辑器的配合体验过程》文章简要介绍了AOP基础概念,包括Before/Around通知、PointCut切入点、Advice通知体、JoinPoint连接点等,说明它们... 目录BeforeAroundAdvise — 通知PointCut — 切入点Acpect — 切面

Spring Security 前后端分离场景下的会话并发管理

《SpringSecurity前后端分离场景下的会话并发管理》本文介绍了在前后端分离架构下实现SpringSecurity会话并发管理的问题,传统Web开发中只需简单配置sessionManage... 目录背景分析传统 web 开发中的 sessionManagement 入口ConcurrentSess

C#异步编程ConfigureAwait的使用小结

《C#异步编程ConfigureAwait的使用小结》本文介绍了异步编程在GUI和服务器端应用的优势,详细的介绍了async和await的关键作用,通过实例解析了在UI线程正确使用await.Conf... 异步编程是并发的一种形式,它有两大好处:对于面向终端用户的GUI程序,提高了响应能力对于服务器端应

MySQL中处理数据的并发一致性的实现示例

《MySQL中处理数据的并发一致性的实现示例》在MySQL中处理数据的并发一致性是确保多个用户或应用程序同时访问和修改数据库时,不会导致数据冲突、数据丢失或数据不一致,MySQL通过事务和锁机制来管理... 目录一、事务(Transactions)1. 事务控制语句二、锁(Locks)1. 锁类型2. 锁粒

深入解析Java NIO在高并发场景下的性能优化实践指南

《深入解析JavaNIO在高并发场景下的性能优化实践指南》随着互联网业务不断演进,对高并发、低延时网络服务的需求日益增长,本文将深入解析JavaNIO在高并发场景下的性能优化方法,希望对大家有所帮助... 目录简介一、技术背景与应用场景二、核心原理深入分析2.1 Selector多路复用2.2 Buffer