【聊聊原子性,中断,以及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}`)

        // r
        let 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}`)
        // r
        let resultCopy = result;
        // 让出cpu
        await 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等待 都不能被切出。这段代码需要完整的执行,这才是我们预期的一段代码的原子性。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

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

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

OBS 免费的录屏软件

一、下载 obs 【OBS】OBS Studio 的安装、参数设置和录屏、摄像头使用教程-CSDN博客 二、使用 obs & 输出无黑屏 【OBS任意指定区域录屏的方法-哔哩哔哩】 https://b23.tv/aM0hj8A OBS任意指定区域录屏的方法_哔哩哔哩_bilibili 步骤&#xff1a; 1&#xff09;获取区域…

Qt源码分析:窗体绘制与响应

作为一套开源跨平台的UI代码库&#xff0c;窗体绘制与响应自然是最为基本的功能。在前面的博文中&#xff0c;已就Qt中的元对象系统(反射机制)、事件循环等基础内容进行了分析&#xff0c;并捎带阐述了窗体响应相关的内容。因此&#xff0c;本文着重分析Qt中窗体绘制相关的内容…

Vue3快速上手--3小时掌握

1. Vue3简介 2020年9月18日&#xff0c;Vue.js发布版3.0版本&#xff0c;代号&#xff1a;One Piece&#xff08;n经历了&#xff1a;4800次提交、40个RFC、600次PR、300贡献者官方发版地址&#xff1a;Release v3.0.0 One Piece vuejs/core截止2023年10月&#xff0c;最新的…

阿里Nacos下载、安装(保姆篇)

文章目录 Nacos下载版本选择Nacos安装Windows常见问题解决 更多相关内容可查看 Nacos下载 Nacos官方下载地址&#xff1a;https://github.com/alibaba/nacos/releases 码云拉取&#xff08;如果国外较慢或者拉取超时可以试一下国内地址&#xff09; //国外 git clone https:…

数组-长度最小的子数组

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; // 子数组…

美团实习—后端开发凉经

面试经历分享 日期&#xff1a; 4月22日时长&#xff1a; 50分钟 意外之喜 没想到在面试过程中&#xff0c;我再次被选中进行下一轮&#xff0c;这确实让我感到有些意外和欣喜。这次面试经历对我而言&#xff0c;不仅是一次技能的检验&#xff0c;更是一次知…

MySQL:设计数据库与操作

设计数据库 1. 数据建模1.1 概念模型1.2 逻辑模型1.3 实体模型主键外键外键约束 2. 标准化2.1 第一范式2.2 链接表2.3 第二范式2.4 第三范式 3. 数据库模型修改3.1 模型的正向工程3.2 同步数据库模型3.3 模型的逆向工程3.4 实际应用建议 4. 数据库实体模型4.1 创建和删除数据库…

10.8K star!史上最强Web应用防火墙雷池WAF

长亭雷池SafeLine是长亭科技耗时近 10 年倾情打造的WAF(Web Application Firewall)&#xff0c; 一款敢打出口号 “不让黑客越雷池一步” 的 WAF&#xff0c;愿称之为史上最强的一款Web应用防火墙&#xff0c;足够简单、足够好用、足够强的免费且开源的 WAF&#xff0c;基于业…

leetcode-20-回溯-切割、子集

一、[131]分割回文串 给定一个字符串 s&#xff0c;将 s 分割成一些子串&#xff0c;使每个子串都是回文串。 返回 s 所有可能的分割方案。 示例: 输入: "aab" 输出: [ ["aa","b"], ["a","a","b"] ] 分析&…

JAVA连接FastGPT实现流式请求SSE效果

FastGPT 是一个基于 LLM 大语言模型的知识库问答系统&#xff0c;提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排&#xff0c;从而实现复杂的问答场景&#xff01; 一、先看效果 真正实流式请求&#xff0c;SSE效果&#xff0c;SSE解释&am…

一切为了安全丨2024中国应急(消防)品牌巡展武汉站成功召开!

消防品牌巡展武汉站 6月28日&#xff0c;由中国安全产业协会指导&#xff0c;中国安全产业协会应急创新分会、应急救援产业网联合主办&#xff0c;湖北消防协会协办的“一切为了安全”2024年中国应急(消防)品牌巡展-武汉站成功举办。该巡展旨在展示中国应急&#xff08;消防&am…

Python基础002

Python数据类型 1、字符串&#xff08;str&#xff09; str3 """I miss you so much""" print("str3 ", str3,type(str3)) str3 I miss you so much <class str>2、整数&#xff08;int&#xff09; str1 55 print(&quo…

【面试题】TLS和SSL协议的区别

TLS&#xff08;Transport Layer Security&#xff09;和SSL&#xff08;Secure Sockets Layer&#xff09;协议都是用于在网络上建立安全通信连接的协议&#xff0c;但它们在多个方面存在区别。以下是TLS和SSL协议之间区别的详细分析&#xff1a; 1. 发展历程与标准化 SSL&a…

如何找BMS算法、BMS软件的实习

之前一直忙&#xff0c;好久没有更新了&#xff0c;今天就来写一篇文章来介绍如何找BMS方向的实习&#xff0c;以及需要具备哪些条件&#xff0c;我的实习经历都是在读研阶段找的&#xff0c;读研期间两段的实习经历再加上最高影响因子9.4分的论文&#xff0c;我的秋招可以说是…

分子AI预测赛Task2笔记

下面所述比较官方的内容都来自官方文档 ‍‌⁠‌‍​​​‌​​⁠​​​​​&#xfeff;​​​&#xfeff;‍‬​​‍⁠‍‍​​‬​&#xfeff;‌​​​‌‍‬​​​​​​‍‌Task2&#xff1a;赛题深入解析 - 飞书云文档 (feishu.cn) 赛题背景 强调了人工智能在科研领域&…

探囊取物之多形式注册页面(基于BootStrap4)

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

晚上睡觉要不要关路由器?一语中的

前言 前几天小白去了一个朋友家&#xff0c;有朋友说&#xff1a;路由器不关机的话会影响睡眠吗&#xff1f; 这个影响睡眠嘛&#xff0c;确实是会的。毕竟一时冲浪一时爽&#xff0c;一直冲浪一直爽……刷剧刷抖音刷到根本停不下来&#xff0c;肯定影响睡眠。 所以晚上睡觉要…

MQTT协议详述

MQTT 概述 消息队列遥测传输&#xff08;英语&#xff1a;Message Queuing Telemetry Transport&#xff0c;缩写&#xff1a;MQTT&#xff09;&#xff0c;是基于发布&#xff08;Publish&#xff09;/订阅&#xff08;Subscribe&#xff09;范式的消息协议&#xff0c;位于…

BurpSuite抓IOS设备HTTPS流量

一、简述&#xff1a; Burp 这个工具做过 web 安全的人都应该用过&#xff0c;是个非常强大的抓包工具。在 PC 的浏览器上直接配置代理就行了&#xff0c;本篇文章就来介绍一下如何用 Burp 抓 IOS 设备上的流量&#xff0c;很多文章都介绍过怎么抓包&#xff0c;但是很多坑都没…