Node原子计数器

2024-08-31 06:12
文章标签 原子 node 计数器

本文主要是介绍Node原子计数器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 基础
    • Automics
    • Mutex
    • 异常并发 case 非原子
    • 正常操作 case 原子

基础

node 并发node通过单线程来处理高并发的请求。

一个事件循环中的执行是可以保证并发安全的,但是也业务操作并发读写一样会有业务的并发问题

在 JavaScript 中,函数总是运行到完成。这意味着如果一个函数正在运行,那么它将完全运行; 只有在这之后,才会调用另一个函数。因此,语句之间不存在交织的可能性(但是对于 Java 来说就不同了)。

单线程 eventLoop

线程锁:单线程编程模式下请求是顺序的,一个好处是不需要考虑线程安全、资源竞争问题,因此当你进行 Node.js 编程时,也不会去考虑线程安全问题。那么多线程编程模式下,例如 Java 你可能很熟悉一个词 synchronized,通常也是 Java 中解决并发编程最简单的一种方式,synchronized 可以保证在同一时刻仅有一个线程去执行某个方法或某块代码。

进程锁:一个服务部署于一台服务器,同时开启多个进程,Node.js 编程中为了利用操作系统资源,根据 CPU 的核心数可以开启多进程模式,这个时候如果对一个共享资源操作还是会遇到资源竞争问题,另外每一个进程都是相互独立的,拥有自己独立的内存空间。关于进程锁通过 Java 中的 synchronized 也很难去解决,synchronized 仅局限于在同一个 JVM 中有效。

分布式锁:一个服务无论是单线程还是多进程模式,当多机部署、处于分布式环境下对同一共享资源进行操作还是会面临同样的问题。此时就要去引入一个概念分布式锁。如下图所示,由于先读数据在通过业务逻辑修改之后进行 SET 操作,这并不是一个原子操作,当多个客户端对同一资源进行先读后写操作就会引发并发问题,这时就要引入分布式锁去解决,通常也是一个很广泛的解决方案。

分布式锁

Automics

原子性操作

Atomics.add() - JavaScript | MDN

Atomics in JavaScript - GeeksforGeeks


describe('autoMicNumberCount', () => {const counter = new Int32Array(new SharedArrayBuffer(4));/*** @description 任务数量++* @private*/async function handCurrentTaskAdd() {Atomics.add(counter, 0, 1);}/*** @description 任务数量--* @private*/async function handCurrentTaskSub() {Atomics.sub(counter, 0, 1);}it('autoMicNumberCount test', async () => {const tasks = [];for (let i = 0; i < 10000; i++) {tasks.push(handCurrentTaskAdd());}for (let i = 0; i < 9000; i++) {tasks.push(handCurrentTaskSub());}await Promise.all(tasks);expect(Atomics.load(counter, 0)).toBe(1000);});
});

Mutex

private readonly mutex = new Mutex();private currentTaskComplete = 1;/*** @description 任务数量++* @private*/private async handCurrentTaskAdd() {await this.mutex.runExclusive(async () => {this.currentTaskComplete++;});}/*** @description 任务数量--* @private*/private async handCurrentTaskSub() {await this.mutex.runExclusive(async () => {this.currentTaskComplete--;});}

异常并发 case 非原子

describe('autoMicNumberCount', () => {let a = 1;async function one() {return 1;}async function example() {// 操作被分割成了多个步骤,并且由于await one();的存在,中间可能会插入其他操作,这就打破了原子性。console.log('Adding 1 to a');a += await one();// 修改 a++ 最终结果就是一致的}it('autoMicNumberCount test', async () => {console.log(`Start, a = ${a}`);Promise.all([example(),example(),example(),]).then(() => {console.log(`All done, a = ${a}`);});});
});

正常操作 case 原子

对于JavaScript而言,由于它是单线程的(至少在V8引擎中是这样),因此在没有显式使用异步或并发特性的情况下,函数中的操作通常被认为是原子性的。

下面操作测试结果都是正常的,不过这块代码对于性能也没有特别苛刻要求,自己对底层了解还是不太足够没有特别大的把握,使用Automics放心一点吧

describe('autoMicNumberCount', () => {let count = 0;/*** @description 任务数量++* @private*/function handCurrentTaskAdd() {count++;}/*** @description 任务数量--* @private*/function handCurrentTaskSub() {count--;}it('autoMicNumberCount test', async () => {const tasks = [];for (let index = 0; index < 10000; index++) {tasks.push(handCurrentTaskAdd());}for (let index = 0; index < 9000; index++) {tasks.push(handCurrentTaskSub());}await Promise.all(tasks);expect(count).toBe(1000);});
});
describe('autoMicNumberCount', () => {let count = 0;beforeEach(() => {// 在每个测试之前启用假定时器jest.useFakeTimers();});afterEach(() => {// 在每个测试之后恢复真实的定时器jest.useRealTimers();});/*** @description 任务数量++* @private*/async function handCurrentTaskAdd() {// 定时器延迟 1 毫秒执行setTimeout(() => {count++;}, 1);}/*** @description 任务数量--* @private*/async function handCurrentTaskSub() {setTimeout(() => {count--;}, 1);}it('autoMicNumberCount test', async () => {const tasks = [];for (let index = 0; index < 10000; index++) {tasks.push(handCurrentTaskAdd());}for (let index = 0; index < 9000; index++) {tasks.push(handCurrentTaskSub());}await Promise.all(tasks);// 使用 Jest 的 advanceTimersByTime 方法来推进时间jest.advanceTimersByTime(100); // 推进足够的时间以确保所有回调都已执行// 确保所有 setTimeout 回调都已经执行expect(count).toBe(1000);});
});

这篇关于Node原子计数器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


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

相关文章

nvm如何切换与管理node版本

《nvm如何切换与管理node版本》:本文主要介绍nvm如何切换与管理node版本问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录nvm切换与管理node版本nvm安装nvm常用命令总结nvm切换与管理node版本nvm适用于多项目同时开发,然后项目适配no

Node.js net模块的使用示例

《Node.jsnet模块的使用示例》本文主要介绍了Node.jsnet模块的使用示例,net模块支持TCP通信,处理TCP连接和数据传输,具有一定的参考价值,感兴趣的可以了解一下... 目录简介引入 net 模块核心概念TCP (传输控制协议)Socket服务器TCP 服务器创建基本服务器服务器配置选项服

mac安装nvm(node.js)多版本管理实践步骤

《mac安装nvm(node.js)多版本管理实践步骤》:本文主要介绍mac安装nvm(node.js)多版本管理的相关资料,NVM是一个用于管理多个Node.js版本的命令行工具,它允许开发者在... 目录NVM功能简介MAC安装实践一、下载nvm二、安装nvm三、安装node.js总结NVM功能简介N

Node.js 中 http 模块的深度剖析与实战应用小结

《Node.js中http模块的深度剖析与实战应用小结》本文详细介绍了Node.js中的http模块,从创建HTTP服务器、处理请求与响应,到获取请求参数,每个环节都通过代码示例进行解析,旨在帮... 目录Node.js 中 http 模块的深度剖析与实战应用一、引言二、创建 HTTP 服务器:基石搭建(一

Node.js学习记录(二)

目录 一、express 1、初识express 2、安装express 3、创建并启动web服务器 4、监听 GET&POST 请求、响应内容给客户端 5、获取URL中携带的查询参数 6、获取URL中动态参数 7、静态资源托管 二、工具nodemon 三、express路由 1、express中路由 2、路由的匹配 3、路由模块化 4、路由模块添加前缀 四、中间件

Node Linux相关安装

下载经编译好的文件cd /optwget https://nodejs.org/dist/v10.15.3/node-v10.15.3-linux-x64.tar.gztar -xvf node-v10.15.3-linux-x64.tar.gzln -s /opt/node-v10.15.3-linux-x64/bin/npm /usr/local/bin/ln -s /opt/nod

Lua 脚本在 Redis 中执行时的原子性以及与redis的事务的区别

在 Redis 中,Lua 脚本具有原子性是因为 Redis 保证在执行脚本时,脚本中的所有操作都会被当作一个不可分割的整体。具体来说,Redis 使用单线程的执行模型来处理命令,因此当 Lua 脚本在 Redis 中执行时,不会有其他命令打断脚本的执行过程。脚本中的所有操作都将连续执行,直到脚本执行完成后,Redis 才会继续处理其他客户端的请求。 Lua 脚本在 Redis 中原子性的原因

SQL2005 性能监视器计数器错误解决方法

【系统环境】 windows 2003 +sql2005 【问题状况】 用户在不正当删除SQL2005后会造成SQL2005 性能监视器计数器错误,如下图 【解决办法】 1、在 “开始” --> “运行”中输入 regedit,开启注册表编辑器,定位到 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVer

在Debian 8上安装Node.js的方法

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 简介 Node.js 是一个通用编程的 JavaScript 平台,允许用户快速构建网络应用程序。通过在前端和后端都使用 JavaScript,开发可以更加一致,并且可以在同一个系统中设计。 在本指南中,您将在 Debian 8 服务器上安装 Node.js。Debian 8 包含一个版本的

使用Node-API进行异步任务开发

一、Node-API异步任务机制概述         Node-API异步任务开发主要用于执行耗时操作的场景中使用,以避免阻塞主线程,确保应用程序的性能和响应效率。         1、应用场景: 文件操作:读取大型文件或执行复杂的文件操作时,可以使用异步工作项来避免阻塞主线程。网络请求:当需要进行网络请求并等待响应时,可以使用异步工作项来避免阻塞主线程,从而提高应用程序的响应性能。数据库操