Go_原子操作和锁

news2024/2/23 4:09:15

原子操作和锁

本文先探究并发问题,再探究锁和原子操作解决问题的方式,最后进行对比。

并发问题

首先,我们看一下程序

num++

该程序表面看上去一步就可以运行完成,但是实际上,在计算机中是分三步运行的,如下图

image-20230929173947408

该程序分为三个步骤

①读取当前值:首先,程序需要读取变量 i 的当前值。过程为,将从内存中加载 'i’的值到CPU寄存器中

② 增加值:读取当前值,将存在寄存器中的值加1,而非在 i 的内存地址操作

③ 写回新值:将新的值写回到变量 i 所在的内存地址

假设 i 的初始值为0,调用两个协程运行 i++,理想情况下,i会变成2

运行过程中,会有六步操作,操作的不同顺序也影响着最后的结果

情况1

image-20230929173710410

两个协程依次运行,结果得到的是 2,完美运行

情况2

image-20230929173822254

当两个协程运行的顺序按上图运行,得到的结果是1,结果明显错误。

这就是并发过程中引起的错误。当多个goroutine在没有相互同步的情况下,访问某个共享的资源,同时对该资源进行读写时,就会处于相互竞争的状态,这就是并发中的资源竞争。

针对以上问题,有两种解决方案,一种是,另一种是原子操作

生活中的例子

想象一个场景,只有一间厕所,但有两个人都想上厕所,显然,厕所一个时刻只运行一个人使用。

第一个人使用前先将门锁上,以防外面的人进来,结束后,再把门锁打开,然后第二个人在锁门,上厕所,开锁。

程序改进

针对以上num++程序,我们可以类比操作。

第一个协程操作前上锁,然后进行num++操作,运行结束后,解锁。

接着第二个程序才能获取锁,再运行num++,运行结束后解锁。

go代码如下

var mu sync.Mutex

func mutexAdd(){
    mu.lock()
    num++
    mu.unlock()
}

资源竞争

当多个协程进行资源竞争的时候,在一个协程获取到锁的时候,其余的协程进入阻塞态,等待资源释放。当该协程运行结束后,调度系统将阻塞队列其中的一个协程拿出来去获取锁,这其中涉及到切换上下文操作,需要消耗一定资源时间。

原子操作

原子操作,即不会被打断的操作。

原子操作是不可分割的,在执行完毕之前不会被其他任务或事物中断。

image-20230929173947408

如上图,i++可以分为三个操作,这三个操作均为原子操作。原子操作必须执行完毕后,才能执行下一个操作。

有没有一种可能,把这三个原子操作合成为一个原子操作?

可以的,在go的标准库atomic中提供了一系列原子操作,其中有atomic.AddInt64(&num,1),可以看作将num++中的三步合并成了一步原子操作。

当num++变成一步原子操作后,便不会出现上述提出的并发问题。因为原子操作是必须一步完成的,其中的过程不能和其他程序交错进行。

go代码如下

func atomicAdd(){
    atomic.AddInt64(&num,1)
}

运行对比

单个协程

单个协程在原子操作和加锁操作下的对比

image-20230929181028320

经过对比,可以发现加锁操作步骤多,耗损资源多,运行效率没有原子操作高

多个协程

假设有两个协程同时运行,协程G1先运行,协程G2等待。以下分别是原子操作和加锁操作的区别

原子操作,当协程G1运行结束后,G2操作

image-20230929181058855

  1. 在g1运行的时候,g2循环等待
  2. g1运行结束,g2开始执行程序
  3. 结束

加锁操作,当协程G1运行结束后,G2操作

image-20230929181125025

  1. 在g1运行的时候,g2获取锁失败,进入阻塞队列
  2. g1解锁后,调度系统调度协程g2,g2获取锁,进入临界区,切换上下文环境
  3. 执行程序
  4. 程序执行结束后,解锁,退出临界区

优势分析

原子操作优势

原子操作适用于对共享变量执行非常简单的操作,如递增、递减、设置标志位等。它们的优势在于性能高,在硬件级别上执行,无需上下文切换或内核调度

原子操作劣势

原子操作无法处理复杂的操作序列,也不能实现多个共享变量之间的复合操作。它们通常不能替代锁,特别是在需要执行多个步骤或操作复杂数据结构时。

锁操作优势

锁适用于需要对多个共享变量执行复杂操作的场景,允许实现复杂的并发算法,并确保一致性。

锁操作劣势

而锁操作伴随着上下文切换和内核调度,这会导致一些性能开销。如果不正常使用,还容易导致死锁和竞态条件

针对以上自增操作,显然,原子操作更占优势。

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

如若内容造成侵权/违法违规/事实不符,请联系七分地网进行投诉反馈,一经查实,立即删除!

相关文章

计算机图像处理-中值滤波

非线性滤波 非线性滤波是利用原始图像跟模版之间的一种逻辑关系得到结果,常用的非线性滤波方法有中值滤波和高斯双边滤波,分别对应cv2.medianBlur(src, ksize)方法和cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace[, dst[, borderType]])方法。 …

Linux高性能服务器编程 学习笔记 第九章 IO复用

IO复用使程序能同时监听多个文件描述符,这可以提高程序的性能,通常网络程序在以下情况需要使用IO复用: 1.客户端进程需要同时处理多个socket。 2.客户端进程需要同时处理用户输入和网络连接。 3.TCP服务器要同时处理监听socket和连接socket…

c#设计模式-结构型模式 之装饰者模式

🚀介绍 在装饰者模式中,装饰者类通常对原始类的功能进行增强或减弱。这种模式是在不必改变原始类的情况下,动态地扩展一个对象的功能。这种类型的设计模式属于结构型模式,因为这种模式涉及到两个类型之间的关系,这两个…

《C++ Primer》练习7.31:定义互相嵌套的类

类可以先声明再定义,可以用下面这个题目理解一下 class Y; class X {Y *y; };class Y {X x; };未出现的类类型要在前面声明。 参考 《C Primer》

前端架构师之02_ES6_高级

1 类和继承 1.1 class类 JavaScript 语言中,生成实例对象的传统方法是通过构造函数。 // ES5 创建对象 // 创建一个类,用户名 密码 function User(name,pass){// 添加属性this.name name;this.pass pass; } // 用 原型 添加方法 User.prototype.sho…

集合-ArrayList源码分析(面试)

系列文章目录 1.集合-Collection-CSDN博客​​​​​​ 2.集合-List集合-CSDN博客 3.集合-ArrayList源码分析(面试)_喜欢吃animal milk的博客-CSDN博客 目录 系列文章目录 前言 一 . 什么是ArrayList? 二 . ArrayList集合底层原理 总结 前言 大家好,今天给大家讲一下Arra…

使用Qt验证RGB格式

下面我们用不同的颜色来绘制一块矩形区域,来对比学习RGB颜色。 一片漆黑的黑色 黑色在RGB中是三个颜色分量都是0。也就是没有颜色。 下面我们绘制一个水平100个像素,垂直200个像素的矩形区域,颜色设置为黑色。 #ifndef MAINWINDOW_H #def…

基于B2B平台的医疗病历交互系统

目录 前言 一、技术栈 二、系统功能介绍 医院管理 医院注册 医院文章 医生信息 医院注册 医疗安排 院区注册 医院公告 医院工作人员 病人病历 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 进入21世纪,计算机技术迅速向着网络化的、集…

el-menu 导航栏学习(1)

最简单的导航栏学习跳转实例效果: (1)index.js路由配置: import Vue from vue import Router from vue-router import NavMenuDemo from /components/NavMenuDemo import test1 from /components/test1 import test2 from /c…

Qt::图层框架-图片图层-序列图层-QGraphicsPixmapItem

二维矢量动画智能制作软件开发合集 链接:软件开发技术分享及记录合集 个人开发二维矢量动画智能制作软件界面如下: 目录 一、图片序列图层原理 二、图片序列图层代码实现 三、图片序列图层软件测试视频 结束语 一、图片序列图层原理 本软件的11种…

凉鞋的 Godot 笔记 102. 场景与节点的增删改查

在上一篇,我们完成了 Godot 引擎的 Hello World 输出,并且完成了第一个基本循环: 通过这次基本循环的完成,我们获得了一点点的 Godot 使用经验,这非常重要。 有实践经验后再去补充理论 和 先学习理论后去实践相比,前者…

面试总结之Spring篇

一、AOP 1、什么是AOP 1.1、概述 AOP(Aspect-Oriented Programming):面向切面编程,即把一些业务逻辑中的相同代码抽取出来,让业务逻辑更加简练清爽 如果要CRUD写一堆业务,可如何实现业务代码前后进行打印…

2023年中国智能电视柜产量、需求量、市场规模及行业价格走势[图]

电视柜是随着电视机的发展和普及而演变出的家具种类,其主要作用是承载电视机,又称视听柜,随着生活水平的提高,与电视机相配套的电器设备也成为电视柜的收纳对象。 随着智能家具的发展,智能电视机柜的造型和风格都是有了…

前端最新支持四级及以下结构仿企查查、天眼查关联投资机构 股权结构 tree树形结构 控股结构

随着技术的发展,开发的复杂度也越来越高,传统开发方式将一个系统做成了整块应用,经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改,造成牵一发而动全身。通过组件化开发,可以有效实现单…

【大规模 MIMO 检测】基于ADMM的大型MU-MIMO无穷大范数检测研究(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

Cannot download sources:IDEA源码无法下载

问题 Swagger的相关包,无法看到注释; 在class文件的页面,点击下载源码,源码下载不了,IDEA报下面的错误。 报错 Cannot download sources Sources not found for: io.swagger.core.v3:swagger-annotations:2.2.9 解决…

二值贝叶斯滤波计算4d毫米波聚类目标动静属性

机器人学中有些问题是二值问题,对于这种二值问题的概率评估问题可以用二值贝叶斯滤波器binary Bayes filter来解决的。比如机器人前方有一个门,机器人想判断这个门是开是关。这个二值状态是固定的,并不会随着测量数据变量的改变而改变。就像门…

Android Jetpack组件架构:ViewModel的原理

Android Jetpack组件架构:ViewModel的原理 导言 本篇文章是关于介绍ViewModel的,由于ViewModel的使用还是挺简单的,这里就不再介绍其的基本应用,我们主要来分析ViewModel的原理。 ViewModel的生命周期 众所周知,一般…

FFmpeg 命令:从入门到精通 | ffplay 简单过滤器

FFmpeg 命令:从入门到精通 | ffplay 简单过滤器 FFmpeg 命令:从入门到精通 | ffplay 简单过滤器视频旋转视频反转视频旋转和反转音频变速播放视频变速播放音视频同时变速更多参考 FFmpeg 命令:从入门到精通 | ffplay 简单过滤器 本节介绍了简…

亲测可用国产GPT人工智能

分享一些靠谱、可用、可以白嫖的GPT大模型。配合大模型,工作效率都会极大提升。 清华大学ChatGLM 官网: 智谱清言中国版对话语言模型,与GLM大模型进行对话。https://chatglm.cn/开源的、支持中英双语的1300亿参数的对话语言模型&#xff0…