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

【Node.js】worker_threads 多线程

Node.js 中的 worker_threads 模块

worker_threads 模块是 Node.js 中用于创建多线程处理的工具。

尽管 JavaScript 是单线程的,但有时候在处理计算密集型任务或长时间运行的操作时,单线程的运行会导致主线程被阻塞,影响服务器性能。

为了解决这种问题,worker_threads 模块允许我们在同一个进程内创建并运行多个线程,每个线程有自己的事件循环,但共享进程的内存空间。

基本概念

  • 主线程:主线程是 Node.js 程序默认执行代码的地方,通常是单线程运行,执行同步和异步的事件循环。
  • Worker(工作线程):工作线程是与主线程平行执行的额外线程,用于处理复杂、长时间运行的任务,不会阻塞主线程的执行。

何时使用 worker_threads

  • 当需要处理 CPU 密集型 任务(如大型计算、图像处理、数据加密等)时。
  • 当需要保持 异步 I/O 操作的同时,不阻塞主线程时。

基本使用方法

1. 创建一个简单的 Worker

我们可以通过 Worker 类创建工作线程。每个工作线程运行一个独立的 JavaScript 文件。

// main.js
const { Worker } = require('worker_threads');// 创建一个新的 Worker,并指定 worker 执行的脚本文件
const worker = new Worker('./worker.js');// 监听 worker 发回的消息
worker.on('message', (message) => {console.log(`Received from worker: ${message}`);
});// 向 worker 发送消息
worker.postMessage('Start task');// worker.js
const { parentPort } = require('worker_threads');// 监听来自主线程的消息
parentPort.on('message', (message) => {console.log(`Worker received: ${message}`);// 进行一些耗时操作let result = 0;for (let i = 0; i < 1e9; i++) {result += i;}// 将结果发回主线程parentPort.postMessage(result);
});

在这个例子中,主线程(main.js)创建了一个 Worker 线程(worker.js),并通过 parentPort 与其通信。主线程可以向 Worker 发送任务,Worker 在处理完后将结果返回给主线程。

2. 数据通信

主线程和 Worker 通过 postMessage()message 事件来传递数据。可以发送任意可以序列化的 JavaScript 数据类型,如字符串、对象、数组等。

  • 主线程向 Worker 发送消息
worker.postMessage('Some data');
  • Worker 向主线程发送消息
parentPort.postMessage('Some result');

3. 共享内存(SharedArrayBuffer)

worker_threads 支持通过 SharedArrayBuffer 来在多个线程之间共享内存。这种机制可以避免频繁的消息传递开销,提高性能。

// main.js
const { Worker } = require('worker_threads');const sharedBuffer = new SharedArrayBuffer(4);  // 分配 4 字节的共享内存
const sharedArray = new Int32Array(sharedBuffer);const worker = new Worker('./worker.js', { workerData: sharedBuffer });worker.on('message', () => {console.log('Modified shared array:', sharedArray);
});// worker.js
const { parentPort, workerData } = require('worker_threads');const sharedArray = new Int32Array(workerData);// 修改共享数组
sharedArray[0] = 42;parentPort.postMessage('Shared data modified');

这里,SharedArrayBuffer 是共享内存的核心,它允许主线程和 Worker 线程访问相同的内存空间。我们用 Int32Array 对内存进行操作,修改数据后,主线程可以立即读取结果,无需通过消息传递。

4. 工作线程与主线程的生命周期

  • 启动和终止

    • 当创建一个 Worker 实例时,线程会自动启动。
    • 当 Worker 执行完所有任务或调用 worker.terminate() 时,线程会退出。
  • 自动终止
    如果工作线程的事件循环为空(没有待处理的事件),Worker 会自动退出。

worker.terminate().then(() => {console.log('Worker terminated');
});

5. 错误处理

在多线程环境下,处理错误尤为重要。我们可以使用 error 事件来捕获线程中的错误。

worker.on('error', (err) => {console.error('Worker error:', err);
});

如果 Worker 出现错误,会触发 error 事件,主线程可以处理这个错误。

Worker 线程池

虽然 worker_threads 允许我们创建多个 Worker,但直接为每个任务创建一个新的 Worker 可能效率较低。为此,我们可以创建一个 线程池,通过复用 Worker 来处理多个任务。

线程池实现(简单示例):

const { Worker } = require('worker_threads');class ThreadPool {constructor(size) {this.size = size;this.workers = [];this.tasks = [];// 初始化线程池for (let i = 0; i < size; i++) {this.workers.push(this.createWorker());}}createWorker() {const worker = new Worker('./worker.js');worker.on('message', () => {this.executeNextTask(worker);});return worker;}executeNextTask(worker) {if (this.tasks.length === 0) {return;}const task = this.tasks.shift();worker.postMessage(task);}runTask(task) {const availableWorker = this.workers.find(w => w.isIdle);if (availableWorker) {availableWorker.isIdle = false;availableWorker.postMessage(task);} else {this.tasks.push(task);}}
}const pool = new ThreadPool(4);pool.runTask('Task 1');
pool.runTask('Task 2');

在这个简单的示例中,我们创建了一个大小为 4 的线程池,任务可以通过 runTask 方法提交到线程池中。线程池会依次执行任务,并复用空闲的线程。

与其他多线程解决方案的比较

  • child_process 模块:允许在 Node.js 中创建独立的进程,进程间通过消息传递进行通信,但资源隔离更强,消耗较大。相比之下,worker_threads 在线程间共享内存,创建成本和通信成本较低。
  • 异步操作:虽然 Node.js 的异步 I/O 可以通过事件驱动模型来处理大量任务,但对于 CPU 密集型任务,异步操作并不适合,此时可以使用 worker_threads 来实现并行计算。

总结

  • worker_threads 是 Node.js 中用于多线程处理的核心工具。
  • 它允许在单个进程内创建多个线程,线程间可以通过消息传递和共享内存进行通信。
  • 非常适合用于处理计算密集型任务,避免主线程的阻塞。
  • 虽然 worker_threads 增强了并行计算的能力,但需要合理管理线程的创建和销毁,避免线程资源的浪费。

相关文章:

【Node.js】worker_threads 多线程

Node.js 中的 worker_threads 模块 worker_threads 模块是 Node.js 中用于创建多线程处理的工具。 尽管 JavaScript 是单线程的&#xff0c;但有时候在处理计算密集型任务或长时间运行的操作时&#xff0c;单线程的运行会导致主线程被阻塞&#xff0c;影响服务器性能。 为了…...

贪心算法c++

贪心算法C概述 一、贪心算法的基本概念 贪心算法&#xff08;Greedy Algorithm&#xff09;&#xff0c;又名贪婪法&#xff0c;是一种解决优化问题的常用算法。其基本思想是在问题的每个决策阶段&#xff0c;都选择当前看起来最优的选择&#xff0c;即贪心地做出局部最优的决…...

【STM32】 TCP/IP通信协议(3)--LwIP网络接口

LwIP协议栈支持多种不同的网络接口&#xff08;网卡&#xff09;&#xff0c;由于网卡是直接跟硬件平台打交道&#xff0c;硬件不同则处理也是不同。那Iwip如何兼容这些不同的网卡呢&#xff1f; LwIP提供统一的接口&#xff0c;底层函数需要用户自行完成&#xff0c;例如网卡的…...

15分钟学 Python 第39天:Python 爬虫入门(五)

Day 39&#xff1a;Python 爬虫入门数据存储概述 在进行网页爬虫时&#xff0c;抓取到的数据需要存储以供后续分析和使用。常见的存储方式包括但不限于&#xff1a; 文件存储&#xff08;如文本文件、CSV、JSON&#xff09;数据库存储&#xff08;如SQLite、MySQL、MongoDB&a…...

使用Pytorch构建自定义层并在模型中使用

使用Pytorch构建自定义层并在模型中使用 继承自nn.Module类&#xff0c;自定义名称为NoisyLinear的线性层&#xff0c;并在新模型定义过程中使用该自定义层。完整代码可以在jupyter nbviewer中在线访问。 import torch import torch.nn as nn from torch.utils.data import T…...

学习记录:js算法(五十六):从前序与中序遍历序列构造二叉树

文章目录 从前序与中序遍历序列构造二叉树我的思路网上思路 总结 从前序与中序遍历序列构造二叉树 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 示…...

qt使用QDomDocument读写xml文件

在使用QDomDocument读写xml之前需要在工程文件添加&#xff1a; QT xml 1.生成xml文件 void createXml(QString xmlName) {QFile file(xmlName);if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate |QIODevice::Text))return false;QDomDocument doc;QDomProcessin…...

Oracle架构之表空间详解

文章目录 1 表空间介绍1.1 简介1.2 表空间分类1.2.1 SYSTEM 表空间1.2.2 SYSAUX 表空间1.2.3 UNDO 表空间1.2.4 USERS 表空间 1.3 表空间字典与本地管理1.3.1 字典管理表空间&#xff08;Dictionary Management Tablespace&#xff0c;DMT&#xff09;1.3.2 本地管理方式的表空…...

springboot整合seata

一、准备 docker部署seata-server 1.5.2参考&#xff1a;docker安装各个组件的命令 二、springboot集成seata 2.1 引入依赖 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId>&…...

鸿蒙开发(NEXT/API 12)【二次向用户申请授权】程序访问控制

当应用通过[requestPermissionsFromUser()]拉起弹框[请求用户授权]时&#xff0c;用户拒绝授权。应用将无法再次通过requestPermissionsFromUser拉起弹框&#xff0c;需要用户在系统应用“设置”的界面中&#xff0c;手动授予权限。 在“设置”应用中的路径&#xff1a; 路径…...

docker export/import 和 docker save/load 的区别

Docker export/import 和 docker save/load 都是用于容器和镜像的备份和迁移&#xff0c;但它们有一些关键的区别&#xff1a; docker export/import: export 作用于容器&#xff0c;import 创建镜像导出的是容器的文件系统&#xff0c;不包含镜像的元数据丢失了镜像的层级结构…...

明星周边销售网站开发:SpringBoot技术全解析

1系统概述 1.1 研究背景 如今互联网高速发展&#xff0c;网络遍布全球&#xff0c;通过互联网发布的消息能快而方便的传播到世界每个角落&#xff0c;并且互联网上能传播的信息也很广&#xff0c;比如文字、图片、声音、视频等。从而&#xff0c;这种种好处使得互联网成了信息传…...

STM32+ADC+扫描模式

1 ADC简介 1 ADC(模拟到数字量的桥梁) 2 DAC(数字量到模拟的桥梁)&#xff0c;例如&#xff1a;PWM&#xff08;只有完全导通和断开的状态&#xff0c;无功率损耗的状态&#xff09; DAC主要用于波形生成&#xff08;信号发生器和音频解码器&#xff09; 3 模拟看门狗自动监…...

R语言绘制散点图

散点图是一种在直角坐标系中用数据点直观呈现两个变量之间关系、可检测异常值并探索数据分布的可视化图表。它是一种常用的数据可视化工具&#xff0c;我们通过不同的参数调整和包的使用&#xff0c;可以创建出满足各种需求的散点图。 常用绘制散点图的函数有plot()函数和ggpl…...

安装最新 MySQL 8.0 数据库(教学用)

安装 MySQL 8.0 数据库&#xff08;教学用&#xff09; 文章目录 安装 MySQL 8.0 数据库&#xff08;教学用&#xff09;前言MySQL历史一、第一步二、下载三、安装四、使用五、语法总结 前言 根据 DB-Engines 网站的数据库流行度排名&#xff08;2024年&#xff09;&#xff0…...

微信小程序开发-配置文件详解

文章目录 一&#xff0c;小程序创建的配置文件介绍二&#xff0c;配置文件-全局配置-pages 配置作用&#xff1a;注意事项&#xff1a;示例&#xff1a; 三&#xff0c;配置文件-全局配置-window 配置示例&#xff1a; 四&#xff0c;配置文件-全局配置-tabbar 配置核心作用&am…...

TCP/UDP初识

TCP是面向连接的、可靠的、基于字节流的传输层协议。 面向连接&#xff1a;一定是一对一连接&#xff0c;不能像 UDP 协议可以一个主机同时向多个主机发送消息 可靠的&#xff1a;无论的网络链路中出现了怎样的链路变化&#xff0c;TCP 都可以保证一个报文一定能够到达接收端…...

【大数据】在线分析、近线分析与离线分析

文章目录 1. 在线分析&#xff08;Online Analytics&#xff09;定义特点应用场景技术栈 2. 近线分析&#xff08;Nearline Analytics&#xff09;定义特点应用场景技术栈 3. 离线分析&#xff08;Offline Analytics&#xff09;定义特点应用场景技术栈 总结 在线分析&#xff…...

【unity进阶知识9】序列化字典,场景,vector,color,Quaternion

文章目录 前言一、可序列化字典类普通字典简单的使用可序列化字典简单的使用 二、序列化场景三、序列化vector四、序列化color五、序列化旋转Quaternion完结 前言 自定义序列化的主要原因&#xff1a; 可读性&#xff1a;使数据结构更清晰&#xff0c;便于理解和维护。优化 I…...

传奇GOM引擎架设好进游戏后提示请关闭非法外挂,重新登录,如何处理?

今天在架设一个GOM引擎的版本时&#xff0c;进游戏之后刚开始是弹出一个对话框&#xff0c;提示请关闭非法外挂&#xff0c;重新登录&#xff0c;我用的是绿盟登陆器&#xff0c;同时用的也是绿盟插件&#xff0c;刚开始我以为是绿盟登录器的问题&#xff0c;于是就换成原版gom…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

前端导出带有合并单元格的列表

// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】&#xff0c;分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...

OD 算法题 B卷【正整数到Excel编号之间的转换】

文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的&#xff1a;a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...

上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式

简介 在我的 QT/C 开发工作中&#xff0c;合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式&#xff1a;工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...

第一篇:Liunx环境下搭建PaddlePaddle 3.0基础环境(Liunx Centos8.5安装Python3.10+pip3.10)

第一篇&#xff1a;Liunx环境下搭建PaddlePaddle 3.0基础环境&#xff08;Liunx Centos8.5安装Python3.10pip3.10&#xff09; 一&#xff1a;前言二&#xff1a;安装编译依赖二&#xff1a;安装Python3.10三&#xff1a;安装PIP3.10四&#xff1a;安装Paddlepaddle基础框架4.1…...

篇章二 论坛系统——系统设计

目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...

简约商务通用宣传年终总结12套PPT模版分享

IOS风格企业宣传PPT模版&#xff0c;年终工作总结PPT模版&#xff0c;简约精致扁平化商务通用动画PPT模版&#xff0c;素雅商务PPT模版 简约商务通用宣传年终总结12套PPT模版分享:商务通用年终总结类PPT模版https://pan.quark.cn/s/ece1e252d7df...

运行vue项目报错 errors and 0 warnings potentially fixable with the `--fix` option.

报错 找到package.json文件 找到这个修改成 "lint": "eslint --fix --ext .js,.vue src" 为elsint有配置结尾换行符&#xff0c;最后运行&#xff1a;npm run lint --fix...