当前位置: 首页 > 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.dtcms.com/a/350765.html

相关文章:

  • 【JavaEE】文件IO操作
  • MyBatis 从入门到精通:一篇就够的实战指南(Java)
  • 最大子数组和【栈和分治两种思路】
  • Linux简明教程01 基础运维
  • C标准库 ---- locale.h
  • Escrcpy 3.0投屏控制软件使用教程:无线/有线连接+虚拟显示功能详解
  • 什么是生命体AI
  • TCP和UDP的使用场景
  • 【系统分析师】高分论文:论软件需求验证方法及应用
  • 用蒙特卡洛法求解三门问题和Π
  • day20 二叉树part7
  • 20.14 QLoRA微调Whisper-Large-v2终极指南:3倍速训练+显存直降68%调参秘籍
  • CVPR 2025端到端自动驾驶新进展:截断扩散模型+历史轨迹预测实现精准规划
  • 【工具安装使用-Jetson】Jetson Orin Nano 刷机和踩坑总结
  • 如何在IDEA中使用Git
  • 【嵌入式电机控制#进阶4】无感控制(二):观测器导论锁相环(全网最通俗易懂)
  • WAS/WDF资源文件工具
  • C :结构体对齐
  • vue+vite打包后的文件希望放在一个子目录下
  • Python 并发编程全面指南(多线程 多进程 进程池 线程池 协程和异步编程) 队列
  • 【leetcode】82. 删除排序链表中的重复元素(二)
  • 微算法科技(NASDAQ:MLGO)使用预测分析动态调整区块大小,构建可持续的区块链网络
  • Cursor概述及环境配置
  • 博客园-awescnb插件-geek皮肤异常问题修复
  • Java数据结构——8.优先级队列(堆)(PriorityQueue)
  • SOME/IP-SD报文中 Option Format(选项格式)-理解笔记1
  • 使用 NetworkManager 管理 Wi-Fi 热点
  • 无线USB转换器TOS-WLink网盘更新--TOS-WLink使用帮助V1.0.pdf
  • 管理驾驶舱不是面子工程!一文讲清搭建思路和具体步骤
  • 【Java SE】认识String类