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

【聊聊原子性,中断,以及nodejs中的具体示例】

什么是原子性

从一个例子说起, x++ ,读和写 ,

如图假设多线程,线程1和线程2同时操作变量x,进行x++的操作,那么由于写的过程中,都会先读一份x数据到cpu的寄存器中,所以这个时候cpu1 和 cpu2 拿到了相同的变量x,假设初始x值为1,则cpu1拿到的x为1,cpu2拿到的x为1,都操作并写回给x后,x的值为2。

预期加两次,结果为3,但是实际由于多线程同时操作同一个变量了 ,可能产生写覆盖。进一步看,这其中还要再提起一个词,中断。

中断

多线程 - cpu中断

多线程下,常见一个或者多个操作在 CPU 执行时候,中断,切出再切回。

对于多线程来说,程序在运行一段代码的时候,可能会中途切出,这种来回切出和切回,就出现了上面x++的情况。产生了写覆盖的问题。

那么不用多线程,只用单线程,是不是就不会存在中断的问题,是不是就安全了,其实也不安全。因为线程下面还有协程(如python Coroutine),或如nodejs中 event loop,其虽然不会在cpu运算的时候切出,但是会在等待io的时候切出。

单线程 - io中断

单线程下,一个或者多个IO操作执行的过程中,中断,切出再切回。

一个单线程切出的例子,拿nodejs中event loop举例,worker1 和 worker2分别产生event,去累加result,但是在累加的过程中会await sleep 模拟等待io,这会导致由于等待io而引起的中断,切出。

非原子性示例

function sleep(ms: number) {return new Promise(resolve => setTimeout(resolve, ms));
}let result = 0;async function worker1() {let maxtime1 = 1;while(maxtime1 <= 100) {let name = 'worker1';// 执行100次)console.log(`${name} calculate current time ${maxtime1}`)// 开始工作let resultCopy = result;// 让出await sleep(10);resultCopy += 1;result = resultCopy;maxtime1 += 1;}
}async function worker2() {let maxtime2 = 1;while(maxtime2 <= 100) {let name = 'worker2';// 执行100次console.log(`${name} calculate current time ${maxtime2}`)// 开始工作let resultCopy = result;// 让出await sleep(10);resultCopy += 1;result = resultCopy;maxtime2 += 1;}
}(async () => {console.log('start calculate')const startTime = Date.now();Promise.all([worker1(), worker2()]).then(() => {const endTime = Date.now();// 预期是200 ,但是由于会写覆盖,所以最终小于200.console.log(`耗时: ${endTime - startTime}ms`);console.log('result:', result);}).catch((error) => {console.error('A worker failed with error:', error);});
})()

运行结果,通过结果 ,甚至输出结果直接就是100,因为worker1 和 worker2的并行执行,导致每次累加计算前,worker1 和 worker2 都拿到相同的值

那么如何避免这种情况,让worker1的代码片段执行完,再执行的worker2的代码片段,不切出,达到原子性,一种方法就是加锁,下面继续看如何加锁达到原子性,

原子性示例

通过加锁,可以实现代码片段的原子性 ,如下

import { Mutex } from 'async-mutex';
const mutex = new Mutex();function sleep(ms: number) {return new Promise(resolve => setTimeout(resolve, ms));
}let result = 0;async function worker1() {let maxtime1 = 1;// 执行100次while(maxtime1 <= 100) {let name = 'worker1';// 开始工作// 锁住,const release = await mutex.acquire();console.log(`${name} calculate current time ${maxtime1}, before start calulate result: ${result}`)// rlet resultCopy = result;// 让出cpu,这里即使让出,其它worker由于无法获取锁,所以会一直等待await sleep(10);resultCopy += 1;// w result = resultCopy;console.log(`${name} calculate current time ${maxtime1}, after calulate result: ${result}`)release();maxtime1 += 1;}
}async function worker2() {let maxtime2 = 1;// 执行100次while(maxtime2 <= 100) {let name = 'worker2';// 开始工作// 锁住,const release = await mutex.acquire();console.log(`${name} calculate current time ${maxtime2}, before start calulate result: ${result}`)// rlet resultCopy = result;// 让出cpuawait sleep(10);resultCopy += 1;// w result = resultCopy;console.log(`${name} calculate current time ${maxtime2}, after calulate result: ${result}`)release();maxtime2 += 1;}
}(async () => {console.log('start calculate')const startTime = Date.now();Promise.all([worker1(), worker2()]).then(() => {const endTime = Date.now();// 预期是200 ,但是由于会写覆盖,所以最终小于200.console.log(`耗时: ${endTime - startTime}ms`);console.log('result:', result);}).catch((error) => {console.error('A worker failed with error:', error);});
})()

此时,在看输出结果,可以发现由于有锁,worker1 和 worker2是串行累加的,不会在执行累加的过程中切出,所以最终累加的结果是200,符合预期。

同时可以发现,由于加锁,整体串行,会导致整体运行时间增加。这里就不得不多提下,Event Loop 是一种异步编程模型,io切出本身属于提高效率的设计,所以如果不是需要原子性,不是同时操作同一个变量,则没必要加锁降低效率。

结语

总结 ,对于编程中的原子性,如果说一段代码是原子性的,则这段代码无论是cpu 还是 io等待 都不能被切出。这段代码需要完整的执行,这才是我们预期的一段代码的原子性。

相关文章:

【聊聊原子性,中断,以及nodejs中的具体示例】

什么是原子性 从一个例子说起&#xff0c; x &#xff0c;读和写 &#xff0c; 如图假设多线程&#xff0c;线程1和线程2同时操作变量x&#xff0c;进行x的操作&#xff0c;那么由于写的过程中&#xff0c;都会先读一份x数据到cpu的寄存器中&#xff0c;所以这个时候cpu1 和 c…...

常见网络端口号

在网络工程领域&#xff0c;了解和掌握默认端口号是至关重要的。端口号是计算机网络中最基本的概念之 一&#xff0c;用于标识特定的网络服务或应用程序。 1、什么是端口号&#xff1f; 端口号是计算机网络中的一种标识&#xff0c;用于区分不同的网络服务和应用程序。每个端…...

【数值计算库-超长笔记】Python-Mpmath库:高精度数值计算

原文链接&#xff1a;https://www.cnblogs.com/aksoam/p/18279394 更多精彩&#xff0c;关注博客园主页&#xff0c;不断学习&#xff01;不断进步&#xff01; 我的主页 csdn很少看私信&#xff0c;有事请b站私信 博客园主页-发文字笔记-常用 有限元鹰的主页 内容&#xf…...

昇思25天学习打卡营第6天|函数式自动微分

函数式自动微分 相关前置知识复习 深度学习的重点之一是神经网络。而神经网络很重要的一环是反向传播算法&#xff0c;这个算法用于调整神经网络的权重。 反向传播算法 这里举例说明反向传播在做什么。 假设你是一个学生&#xff0c;一次考试过后&#xff0c;你收到了一份老…...

作业7.2

用结构体数组以及函数完成: 录入你要增加的几个学生&#xff0c;之后输出所有的学生信息 删除你要删除的第几个学生&#xff0c;并打印所有的学生信息 修改你要修改的第几个学生&#xff0c;并打印所有的学生信息 查找你要查找的第几个学生&#xff0c;并打印该的学生信息 1 /*…...

PCL 点云聚类(基于体素连通性)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 这里的思路很简单,我们通过将点云转换为体素,基于体素的连通性实现对点云的聚类(有点类似于欧式聚类),不过这种方式进行的聚类有些粗糙,但聚类速度相对会快很多,具体的实现效果可以详细阅读代码。 二、实现代…...

python自动化运维--DNS处理模块dnspython

1.dnspython介绍 dnspython是Pyhton实现的一个DNS工具包&#xff0c;他几乎支持所有的记录类型&#xff0c;可以用于查询、传输并动态更新ZONE信息&#xff0c;同事支持TSIG&#xff08;事物签名&#xff09;验证消息和EDNS0&#xff08;扩展DNS&#xff09;。在系统管理方面&a…...

成人职场商务英语学习柯桥外语学校|邮件中的“备注”用英语怎么说?

在英语中&#xff0c;"备注"通常可以翻译为"Notes" 或 "Remarks"。 这两个词在邮件中都很常用。例如: 1. Notes Notes: 是最通用和最常见的表达&#xff0c;可以用在各种情况下&#xff0c;例如&#xff1a; 提供有关电子邮件内容的附加信息 列…...

AndroidStudio报错macMissing essential plugin

电脑重启后打开studio&#xff1a; Missing essential plugin: org.jetbrains.android Please reinstall Android Studio from scratch. 无法使用 对应Mac下disabled_plugins.txt位于如下目录&#xff1a; /Users/ACB/Library/Application Support/Google/AndroidStudio4.2 …...

doris集群物理部署保姆级教程

doris物理安装 1、安装要求 Linux 操作系统版本需求​ 查看CentOs版本(>7.1) cat /etc/redhat-release 1&#xff09;设置系统最大打开文件句柄数​ vi /etc/security/limits.conf soft nofile 65536hard nofile 65536 echo ‘’’ soft nofile 655360hard nofile 655…...

探囊取物之多形式登录页面(基于BootStrap4)

基于BootStrap4的登录页面&#xff0c;支持手机验证码登录、账号密码登录、二维码登录、其它统一登录 低配置云服务器&#xff0c;首次加载速度较慢&#xff0c;请耐心等候&#xff1b;演练页面可点击查看源码 预览页面&#xff1a;http://www.daelui.com/#/tigerlair/saas/pr…...

【ONLYOFFICE】| 桌面编辑器从0-1使用初体验

目录 一. &#x1f981; 写在前面二. &#x1f981; 在线使用感受2.1 创建 ONLYOFFICE 账号2.2 编辑pdf文档2.3 pdf直接创建表格 三. &#x1f981; 写在最后 一. &#x1f981; 写在前面 所谓桌面编辑器就是一种用于编辑文本、图像、视频等多种自媒体的软件工具&#xff0c;具…...

20、PHP字符串的排列(含源码)

题目&#xff1a; PHP字符串的排列&#xff1f; 描述&#xff1a; 输入一个字符串,按字典序打印出该字符串中字符的所有排列。 例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。 输入描述&#xff1a; 输入一个字符串,长度不超过9(可…...

Linux 标准IO的fopen和fclose

getchar(),putchar() ‐‐‐‐ 一个字符 gets(buf),puts(buf) ‐‐‐‐ 一串字符 scanf(),printf() ‐‐‐‐ 一个字符&#xff0c;一串字符都可以 fopen函数的形式 FILE * fopen(constchar *path , cost char *mode) /* * description : 打开一个文件 * param ‐ path…...

一个计算密集小程序在不同CPU下的表现

本文比较了几款CPU对同一测试程序的比较结果&#xff0c;用的是Oracle公有云OCI上的计算实例&#xff0c;均分配的1 OCPU&#xff0c;内存用的默认值&#xff0c;不过内存对此测试程序运行结果不重要。 本文只列结果&#xff0c;不做任何评价。下表中&#xff0c;最后一列为测…...

圈子系统搭建教程,以及圈子系统的功能特点,圈子系统,允许二开,免费源码,APP小程序H5

圈子是一款社区与群组的交友工具。你可以在软件内创造一个兴趣的群组从而达到按圈子来交友的效果用户可以根据自己的兴趣爱好。 1. 创建圈子 轻松创建专属圈子&#xff0c;支持付费型社群。 2. 加入圈子 加入不同圈子&#xff0c;设置不同名片&#xff0c;保护隐私。 3. 定…...

递归算法练习

112. 路径总和 package Tree;import java.util.HashMap; import java.util.Map;class TreeNode {int val;TreeNode left;TreeNode right;public TreeNode(int val) {this.val val;} }/*** 求 树的路径和* <p>* 递归 递减* <p>* 询问是否存在从*当前节点 root 到叶…...

WebDriver 类的常用属性和方法

目录 &#x1f38d;简介 &#x1f38a;WebDriver 核心概念 &#x1f389;WebDriver 常用属性 &#x1f381;WebDriver 常用方法 &#x1f437;示例代码 &#x1f3aa;注意事项 &#x1f390;结语 &#x1f9e3;参考资料 &#x1f38d;简介 Selenium WebDriver 是一个用…...

基于x86+FPGA+AI轴承缺陷视觉检测系统,摇枕弹簧智能检测系统

一、承缺陷视觉检测系统 应用场景 轴类零件自动检测设备&#xff0c;集光、机、软件、硬件&#xff0c;智能图像处理等先进技术于一体&#xff0c;利用轮廓特征匹配&#xff0c;目标与定位&#xff0c;区域选取&#xff0c;边缘提取&#xff0c;模糊运算等算法实现人工智能高…...

短剧小程序系统cps分销开发搭建

短剧小程序系统CPS分销开发搭建是一个相对复杂但具有广阔商业前景的过程。以下是关于短剧小程序系统CPS分销开发搭建的详细步骤和要点&#xff1a; 需求分析与市场调研&#xff1a; 深入了解市场需求、用户画像和竞品分析&#xff0c;明确产品定位和功能需求。研究目标用户的消…...

代理IP的10大误区:区分事实与虚构

在当今的数字时代&#xff0c;代理已成为在线环境不可或缺的一部分。它们的用途广泛&#xff0c;从增强在线隐私到绕过地理限制。然而&#xff0c;尽管代理无处不在&#xff0c;但仍存在许多围绕代理的误解。在本博客中&#xff0c;我们将探讨和消除一些最常见的代理误解&#…...

数组-长度最小的子数组

M长度最小的子数组&#xff08;leetcode209&#xff09; /*** param {number} target* param {number[]} nums* return {number}*/ var minSubArrayLen function(target, nums) {const n nums.length;let ans n 1;let sum 0; // 子数组元素和let left 0; // 子数组…...

深度学习之交叉验证

交叉验证&#xff08;Cross-Validation&#xff09;是一种用于评估和验证机器学习模型性能的技术&#xff0c;尤其是在数据量有限的情况下。它通过将数据集分成多个子集&#xff0c;反复训练和测试模型&#xff0c;以更稳定和可靠地估计模型的泛化能力。常见的交叉验证方法有以…...

使用 Python 五年后,我发现学 python 必看这三本书!少走一半弯路

第一本 《Python编程-从入门到实践》 适合零基础的读者 豆瓣评分&#xff1a;9.1 推荐指数&#xff1a;5颗星 推荐理由&#xff1a; 本书是针对所有层次的 Python 读者而作的 Python 入门书。全书分为两部分&#xff1a; 第一部分介绍使用Python 编程所必须了解的…...

React@16.x(45)路由v5.x(10)源码(2)- history

目录 1&#xff0c;作用1.1&#xff0c;createBrowserHistory1.2&#xff0c;createHashHistory1.3&#xff0c;createMemoryHistory 2&#xff0c;history 对象的属性2.1&#xff0c;action2.2&#xff0c;push / replace / go / goBack / goForward2.3&#xff0c;location2.…...

grpc学习golang版( 八、双向流示例 )

系列文章目录 第一章 grpc基本概念与安装 第二章 grpc入门示例 第三章 proto文件数据类型 第四章 多服务示例 第五章 多proto文件示例 第六章 服务器流式传输 第七章 客户端流式传输 第八章 双向流示例 文章目录 一、前言二、定义proto文件三、编写server服务端四、编写client客…...

SpringBoot学习05-[SpringBoot的嵌入式Servlet容器]

SpringBoot的嵌入式Servlet容器 嵌入式Servlet容器servlet容器-嵌入式servlet容器配置修改通过全局配置文件修改修改添加实现了WebServerFactoryCustomizer接口的bean来进行修改 servlet容器-注册servlet三大组件应该如何注册呢&#xff1f;servlet3.0规范提供的注解方式进行注…...

查看Oracle是哪个Oracle_home 下启动的

[rootrac1 ~]# ps -ef|grep smon root 413 24903 0 22:30 pts/0 00:00:00 grep --colorauto smon root 27165 1 0 22:11 ? 00:00:09 /u01/app/19.0.0/grid/bin/osysmond.bin grid 27784 1 0 22:12 ? 00:00:00 asm_smon_ASM1 oracl…...

重温react-06(初识函数组件和快速生成格式的插件使用方式)

开始 函数组件必然成为未来发展的趋势(个人见解),总之努力的去学习,才能赚更多的钱.加油呀! 函数组件的格式 import React from reactexport default function LearnFunction01() {return (<div>LearnFunction01</div>) }以上是函数式组件的组基本的方式 快捷生…...

【高考志愿】仪器科学与技术

目录 一、专业介绍 1.1 专业概述 1.2 专业方向 1.3 主要课程 二、专业技能与素质培养 三、就业前景 四、个人发展规划建议 五、仪器科学与技术专业排名 六、总结 一、专业介绍 1.1 专业概述 仪器科学与技术专业是一门综合性极强的学科&#xff0c;它融合了测量、控制…...