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

1. 并发产生背景 并发解决原理

文章目录

  • 一. 并发产生背景
    • 1.1 单核CPU
    • 1.2 多核CPU
    • 1.3 并发引入
  • 二. 并发解决
    • 2.1 总线锁
    • 2.2 缓存行锁
    • 2.3 原子性

一. 并发产生背景

为什么现在普遍流行并发的说法?那何为并发呢?

1.1 单核CPU

​ 那我们思维回到最初始的单核CPU,单核CPU(抛开切换上下文)操控一个数据会产生数据问题么?答案是否定的。因为单核CPU操控数据,只有一个进程在串行的操作数据。那我们从汇编层面看下数据如何操作。就拿对一个内存数据int a = 1进行+1操作举例。

intel cpu举例,因为我们服务器基于intel实现较多,intel的汇编指令是risc,精简指令集,笔者在这里写汇编伪代码,目的是带大家感受底层的操作原理。

addl $1, (%ebp - 4)

而对于CPU而言,它要操作的是微指令(μop),所以risc会被译码成多个步骤的汇编

# 笔者这里省略开辟栈帧,直接写个伪代码,感受汇编的操作
movl (%ebp - 4), eax // 将内存数据,放入eax寄存器中
addl 1, eax 		// 进行+1操作
movl eax, (%ebp - 4)  // 放回内存

所以就通过简单的单核CPU就可看出,它操作一个内存数据,是要有大致的3个步骤。

有了这节的铺垫,我们可以更好后面章节的并发引入。

1.2 多核CPU

拿2个CPU举例,同时操作一个内存数据,会发生什么?

首先,我们需要分2个场景

  • 场景1:读数据

若2个CPU同时读一个内存数据,而不进行更改,那会不会产生数据问题?这就可以联想一个上层应用的场景,读redis缓存数据,是否会考虑并发问题?

答案是否定的,因为这里只涉及读时的性能问题,而不会导致实质性的数据问题。

  • 场景2:写数据

ps:这里就要引入[1.1 单核CPU](# 1.1 单核CPU)时讲解的汇编指令,但读者也不用担心会非常难以理解,因为笔者也不是汇编层面的开发人员。这里只是做个了解。

​ 当2个CPU同时执行自己的cu,alu,mu来开始操作写数据,进行+1操作时,会发生什么?

​ 比如一个CPU执行+1操作,会衍生3条汇编指令,而2个CPU同时CPU同时操作,每个CPU3条汇编指令,那么总共是6条汇编指令。在同一时刻有可能是CPU-1执行了第一条汇编,而CPU-2执行到了第2条汇编,有诸多排列组合。

​ 若此时,CPU-1,CPU-2,都把内存数据读到了各自的EAX中,这时内存数据读取的操作都是一样,若同时进行+1操作时,CPU-1执行后是2,CPU-2执行后也是2,那么写回内存,最后结果就是2,而非期望的结果3。

1.3 并发引入

​ 由前面2节引申的概念,可以推理出,当多核CPU同时执行操作一个可读可写的数据,会导致数据紊乱,而造成紊乱的原因:汇编指令的穿插执行。

二. 并发解决

2.1 总线锁

​ 既然是穿插执行导致的数据紊乱问题,那么我们不让它紊乱就行了。比如CPU执行这个数据读/写内存,需要通过数据总线,控制总线,地址总线。多核CPU共享总线,那么我们就把总线锁住是否可以?答案是肯定的,因为CPU-1执行时,把总线锁住,导致CPU-2操作不了该数据。这就叫总线锁

​ 那么会衍生出一个新的问题。若其他CPU不操作该数据,也会被锁住阻塞等待。直至CPU-1操作完,这样的操作很消耗性能,减少吞吐量。

2.2 缓存行锁

​ 那么为了解决这种粗粒度的锁性能问题,但又想保证汇编指令的不可被插入性,那该怎么办呢?

​ 每个CPU内部都有自己的L1缓存行,若我把数据的虚拟地址读入到缓存行中,进行缓存行的锁定该数据虚拟地址,那么其他CPU若访问该虚拟地址就会被阻塞,性能就会增加,这就叫缓存行锁。

在这里插入图片描述

2.3 原子性

​ 上面引申了2个锁的概念,现在就需要说明一下汇编指令的不可插入行为的术语,该术语叫做原子性。

原子性:一段指令要么不执行,要么都执行完毕并且执行时不被穿插别的指令。也就是一串不被拆散的操作。

那么每个CPU厂商肯定要实现自己的原子性操作,会提供CPU级别的原子性指令来保证并发操作数据时带来的紊乱性。那么上层应用使用该CPU提供的原子性操作,即可实现自己的并发操作保证多段指令间的并发问题。

http://www.xdnf.cn/news/1366219.html

相关文章:

  • pytest 并发执行用例(基于受限的测试资源)
  • 现代C++工具链实战:CMake + Conan + vcpkg依赖管理
  • week4-[一维数组]数码个数
  • k8s笔记02概述
  • C++|UDP通讯使用总结
  • HTML应用指南:利用GET请求获取MSN 天气数据并可视化
  • [系统架构设计师]应用数学(二十一)
  • list容器的使用
  • GNN:用MPNN(消息传递神经网络)落地最短路径问题模型训练全流程
  • 用 GSAP + ScrollTrigger 打造沉浸式视频滚动动画
  • 【Day 33】Linux-Mysql日志
  • DDR3入门系列(二)------DDR3硬件电路及Xilinx MIG IP核介绍
  • linux 正则表达式学习
  • 使用 gemini 来分析 github 项目
  • 安卓11 12系统修改定制化_____修改固件 默认给指定内置应用系统级权限
  • 大模型的思考方式
  • Java全栈开发实战:从Spring Boot到Vue3的项目实践
  • ZKmall开源商城多端兼容实践:鸿蒙、iOS、安卓全平台适配的技术路径
  • 8.25作业
  • [MH22D3开发笔记]2. SPI,QSPI速度究竟能跑多快,双屏系统的理想选择
  • Linux笔记9——shell编程基础-3
  • Tesseract OCR之页面布局分析
  • Linux系统的网络管理(一)
  • c# 读取xml文件内的数据
  • 网络编程-HTTP
  • zookeeper-znode解析
  • 【动态规划】309. 买卖股票的最佳时机含冷冻期及动态规划模板
  • 深入浅出 ArrayList:从基础用法到底层原理的全面解析(中)
  • 【C语言16天强化训练】从基础入门到进阶:Day 11
  • 信号处理的核心机制:从保存、处理到可重入性与volatile