Go_原子操作和锁

原子操作和锁

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

并发问题

首先,我们看一下程序

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.Mutexfunc 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://www.rhkb.cn/news/145466.html

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

相关文章

相机数据恢复!详细步骤解析(2023新版)

和朋友在外面旅游用相机拍了好多有意义的照片和视频,但是导入电脑后不知道是被我删除了还是什么原因,这些照片都不见了,请问有方法恢复吗?” 在数字摄影时代,我们依赖相机记录珍贵的瞬间。然而,相机数据丢失…

LeNet网络复现

文章目录 1. LeNet历史背景1.1 早期神经网络的挑战1.2 LeNet的诞生背景 2. LeNet详细结构2.1 总览2.2 卷积层与其特点2.3 子采样层(池化层)2.4 全连接层2.5 输出层及激活函数 3. LeNet实战复现3.1 模型搭建model.py3.2 训练模型train.py3.3 测试模型test…

MyBatisPlus(七)等值查询

等值查询 条件查询&#xff1a;使用 Wrapper 对象&#xff0c;传递查询条件。 QueryWrapper&#xff08;不要使用&#xff09; 代码 Testvoid eq() {QueryWrapper<User> wrapper new QueryWrapper<>();wrapper.eq("name", "张三");List<…

httpserver 下载服务器demo

实现效果如下&#xff1a; 图片可以直接显示 cpp h 这些可以直接显示 其他的 则是提示是否要下载 单线程 还有bug 代码如下 先放上来 #include "httpserver.h" #include "stdio.h" #include <stdlib.h> #include <arpa/inet.h> #include…

Vue控制textarea可输入行数限制-案例

控制只能输入六行内容 UI部分代码 //我使用了antd ui库 <a-form-model-item ref"address_group" label"规则描述" prop"address_group" > <a-textarea:rows"6"style"width: 60%"placeholder"一次最多输入6行…

【数据结构】队列和栈

大家中秋节快乐&#xff0c;玩了好几天没有学习&#xff0c;今天分享的是栈以及队列的相关知识&#xff0c;以及栈和队列相关的面试题 1.栈 1.1栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作…

MySQL数据查询性能如何分析--Explain介绍说明

1、Explain是什么 Explain是MySQL执行查看执行计划命令的指令&#xff0c;使用EXPLAIN关键字可以模拟优化器执行SQL查询语句&#xff0c;从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈。 2、Explain官网介绍 http://dev.mysql.com/doc/refma…

【MySQL】数据类型(二)

文章目录 一. char字符串类型二. varchar字符串类型2.1 char和varchar比较 三. 日期和时间类型四. enum和set类型4.1 set的查询 结束语 一. char字符串类型 char (L) 固定长度字符串 L是可以存储的长度&#xff0c;单位是字符&#xff0c;最大长度是255 MySQL中的字符&#xff…

Makefile学习

一、Makefile的介绍 1.1 什么是Makefile 相信在Linux系统中经常会用到make这个命令来编译程序&#xff0c;而执行make命令所依赖的文件便是Makefile文件&#xff0c;make命令通过Makefile文件编写的内容对程序进行编译。make命令根据文件更新的时间戳来决定哪些文件需要重新编…

纯css html 真实水滴效果

惯例,不多说直接上图 秉承着开源精神,我们将这段代码无私地分享给大家&#xff0c;因为我们深信&#xff0c;信息的共享和互相学习是推动科技进步的关键。我们鼓励大家在使用这段代码的同时&#xff0c;也能够将其中的原理、思想和经验分享给更多的人。 这份代码是我们团队用心…

一百八十六、大数据离线数仓完整流程——步骤五、在Hive的DWS层建动态分区表并动态加载数据

一、目的 经过6个月的奋斗&#xff0c;项目的离线数仓部分终于可以上线了&#xff0c;因此整理一下离线数仓的整个流程&#xff0c;既是大家提供一个案例经验&#xff0c;也是对自己近半年的工作进行一个总结。 二、数仓实施步骤 &#xff08;五&#xff09;步骤五、在Hive的…

Purism 推出注重隐私的 Linux 平板电脑

导读一款昂贵的 Linux 平板电脑&#xff0c;注重安全和隐私。让我们拭目以待。 Purism 是一家日益流行的计算机硬件产品制造商&#xff0c;专门提供配备注重隐私的开源 Linux 发行版的笔记本电脑、台式机和移动设备。 最近&#xff0c;他们发布了一款新产品 Librem 11 平板电…

ARM底层汇编基础指令

汇编语言的组成 伪操作 不参与程序执行&#xff0c;但是用于告诉编译器程序怎么编译.text .global .end .if .else .endif .data 汇编指令 编译器将一条汇编指令编译成一条机器码&#xff0c;在内存里一条指令占4字节内存&#xff0c;一条指令可以实现一个特定的功能 伪指令 不…

嵌入式Linux应用开发-基础知识-第十六章GPIO和Pinctrl子系统的使用

嵌入式Linux应用开发-基础知识-第十六章GPIO和Pinctrl子系统的使用 第十六章 GPIO 和 Pinctrl 子系统的使用16.1 Pinctrl 子系统重要概念16.1.1 引入16.1.2 重要概念16.1.3 示例16.1.4 代码中怎么引用pinctrl 16.2 GPIO子系统重要概念16.2.1 引入16.2.2 在设备树中指定引脚16.2…

软件设计模式系列之二十一——观察者模式

1 观察者模式的定义 观察者模式&#xff08;Observer Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许对象之间建立一对多的依赖关系&#xff0c;当一个对象的状态发生变化时&#xff0c;所有依赖于它的对象都会得到通知并自动更新。这个模式也被称为发布-订阅模式…

数据结构 | 二叉树

基本形状 可参照 数据结构&#xff1a;树(Tree)【详解】_数据结构 树_UniqueUnit的博客-CSDN博客 二叉树的性质 三种顺序遍历

BUUCTF reverse wp 51 - 55

findKey shift f12 找到一个flag{}字符串, 定位到关键函数, F5无效, 大概率是有花指令, 读一下汇编 这里连续push两个byte_428C54很奇怪, nop掉下面那个, 再往上找到函数入口, p设置函数入口, 再F5 LRESULT __stdcall sub_401640(HWND hWndParent, UINT Msg, WPARAM wPara…

Kafka(一)使用Docker Compose安装单机Kafka以及Kafka UI

文章目录 Kafka中涉及到的术语Kafka镜像选择Kafka UI镜像选择Docker Compose文件Kafka配置项说明KRaft vs Zookeeper和KRaft有关的配置关于Controller和Broker的概念解释Listener的各种配置 Kafka UI配置项说明 测试Kafka集群Docker Compose示例配置 Kafka中涉及到的术语 对于…

Spring5应用之AOP切入点详解

作者简介&#xff1a;☕️大家好&#xff0c;我是Aomsir&#xff0c;一个爱折腾的开发者&#xff01; 个人主页&#xff1a;Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客 当前专栏&#xff1a;Spring5应用专栏_Aomsir的博客-CSDN博客 文章目录 前言切入点详解切…

简单三步 用GPT-4和Gamma自动生成PPT PDF

1. 用GPT-4 生产PPT内容 我想把下面的文章做成PPT&#xff0c;请你给出详细的大纲和内容 用于谋生的知识&#xff0c;学生主要工作是学习&#xff0c;成年人的工作是养家糊口&#xff0c;这是基本的要求&#xff0c;在这之上&#xff0c;才能有更高的追求。 不要短期期望过高…