Node.js 单线程与多进程

大家都知道 Node.js 性能很高,是以异步事件驱动、非阻塞 I/O 而被广泛使用。但缺点也很明显,由于 Node.js 是单线程程序,如果长时间运算,会导致 CPU 不能及时释放,所以并不适合 CPU 密集型应用。

当然,也不是没有办法解决这个问题。虽然 Node.js 不支持多线程,但是可创建多子进程来执行任务。
Node.js 提供了 child_process 和 cluster 两个模块可用于创建多子进程

下面我们就分别使用单线程和多进程来模拟查找大量斐波那契数进行 CPU 密集测试

以下代码是查找 500 次位置为 35 的斐波那契数(方便测试,定了一个时间不需要太长也不会太短的位置)

单线程处理

代码:single.js

function fibonacci(n) {
 if (n == 0 || n == 1) {
  return n;
 } else {
  return fibonacci(n - 1)   fibonacci(n - 2);
 }
}

let startTime = Date.now();
let totalCount = 500;
let completedCount = 0;
let n = 35;

for (let i = 0; i < totalCount; i  ) {
 fibonacci(n);
 completedCount  ;
 console.log(`process: ${completedCount}/${totalCount}`);
}
console.log("👏 👏 👏 👏 👏 👏 👏 👏 👏 👏");
console.info(`任务完成,用时: ${Date.now() - startTime}ms`);
console.log("👏 👏 👏 👏 👏 👏 👏 👏 👏 👏");

执行node single.js 查看结果

在我的电脑上显示结果为44611ms(电脑配置不同也会有差异)。

...
process: 500/500
👏 👏 👏 👏 👏 👏 👏 👏 👏 👏
任务完成,用时: 44611ms
👏 👏 👏 👏 👏 👏 👏 👏 👏 👏

查找 500 次需要 44 秒,太慢了。可想而知如果位置更大,数量更多...

那我们来尝试用多进程试试 ⬇️

多进程

采用 cluster 模块,Master-Worker 模式来测试

共 3 个 js,分别为主线程代码:master.js、子进程代码:worker.js、入口代码:cluster.js(入口可无需单独写一个 js、这里是为了看起来更清楚一些)

主线程代码:master.js

const cluster = require("cluster");
const numCPUs = require("os").cpus().length;

// 设置子进程执行程序
cluster.setupMaster({
 exec: "./worker.js",
 slient: true
});

function run() {
 // 记录开始时间
 const startTime = Date.now();
 // 总数
 const totalCount = 500;
 // 当前已处理任务数
 let completedCount = 0;
 // 任务生成器
 const fbGenerator = FbGenerator(totalCount);

 if (cluster.isMaster) {
  cluster.on("fork", function(worker) {
   console.log(`[master] : fork worker ${worker.id}`);
  });
  cluster.on("exit", function(worker, code, signal) {
   console.log(`[master] : worker ${worker.id} died`);
  });

  for (let i = 0; i < numCPUs; i  ) {
   const worker = cluster.fork();

   // 接收子进程数据
   worker.on("message", function(msg) {
    // 完成一个,记录并打印进度
    completedCount  ;
    console.log(`process: ${completedCount}/${totalCount}`);

    nextTask(this);
   });

   nextTask(worker);
  }
 } else {
  process.on("message", function(msg) {
   console.log(msg);
  });
 }

 /**
  * 继续下一个任务
  *
  * @param {ChildProcess} worker 子进程对象,将在此进程上执行本次任务
  */
 function nextTask(worker) {
  // 获取下一个参数
  const data = fbGenerator.next();
  // 判断是否已经完成,如果完成则调用完成函数,结束程序
  if (data.done) {
   done();
   return;
  }
  // 否则继续任务
  // 向子进程发送数据
  worker.send(data.value);
 }

 /**
  * 完成,当所有任务完成时调用该函数以结束程序
  */
 function done() {
  if (completedCount >= totalCount) {
   cluster.disconnect();
   console.log("👏 👏 👏 👏 👏 👏 👏 👏 👏 👏");
   console.info(`任务完成,用时: ${Date.now() - startTime}ms`);
   console.log("👏 👏 👏 👏 👏 👏 👏 👏 👏 👏");
  }
 }
}

/**
 * 生成器
 */
function* FbGenerator(count) {
 var n = 35;
 for (var i = 0; i < count; i  ) {
  yield n;
 }
 return;
}

module.exports = {
 run
};

1.这里是根据当前电脑的逻辑 CPU 核数来创建子进程的,不同电脑数量也会不一样,我的 CPU 是 6 个物理核数,由于支持超线程处理,所以逻辑核数是 12,故会创建出 12 个子进程

2.主线程与子进程之间通信是通过send方法来发送数据,监听message事件来接收数据

3.不知道大家有没有注意到我这里使用了 ES6 的 Generator 生成器来模拟生成每次需要查找的斐波那契数位置(虽然是写死的 😂,为了和上面的单线程保证统一)。这么做是为了不让所有任务一次性扔出去,因为就算扔出去也会被阻塞,还不如放在程序端就给控制住,完成一个,放一个。

子进程代码:worker.js

function fibonacci(n) {
 if (n == 0 || n == 1) {
  return n;
 } else {
  return fibonacci(n - 1)   fibonacci(n - 2);
 }
}

// 接收主线程发送过来的任务,并开始查找斐波那契数
process.on("message", n => {
 var res = fibonacci(n);
 // 查找结束后通知主线程,以便主线程再度进行任务分配
 process.send(res);
});
入口代码:cluster.js
// 引入主线程js,并执行暴露出来的run方法
const master = require("./master");
master.run();

执行node cluster.js 查看结果

在我的电脑上显示结果为10724ms(电脑配置不同也会有差异)。

process: 500/500
👏 👏 👏 👏 👏 👏 👏 👏 👏 👏
任务完成,用时: 10724ms
👏 👏 👏 👏 👏 👏 👏 👏 👏 👏

结果

进过上面两种方式的对比,结果很明显,多进程处理速度是单线程处理速度的 4 倍多。而且有条件的情况下,如果电脑 CPU 足够,进程数更多,那么速度也会更快。

如果有更好的方案或别的语言能处理你的需求那就更好,谁让 Node.js 天生就不适合 CPU 密集型应用呢。。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持Devmax。

Node.js 多进程处理CPU密集任务的实现的更多相关文章

  1. CentOS 8.2服务器上安装最新版Node.js的方法

    这篇文章主要介绍了CentOS 8.2服务器上安装最新版Node.js的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  2. node.js三个步骤实现一个服务器及Express包使用

    这篇文章主要介绍了node.js三个步骤实现一个服务器及Express包使用,文章通过新建一个文件展开全文内容,具有一定的参考价值,需要的小伙伴可以参考一下

  3. Node.js调试技术总结分享

    Node.js是一个可以快速构建网络服务及应用的平台。该平台的构建是基于Chrome's JavaScript runtime,也就是说,实际上它是对Google V8引擎(应用于Google Chrome浏览器)进行了封装。 今天介绍Node.js调式目前有几种技术,需要的朋友可以参考下。

  4. node.js实现http服务器与浏览器之间的内容缓存操作示例

    这篇文章主要介绍了node.js实现http服务器与浏览器之间的内容缓存操作,结合实例形式分析了node.js http服务器与浏览器之间的内容缓存原理与具体实现技巧,需要的朋友可以参考下

  5. 教你如何使用node.js制作代理服务器

    本文介绍了如何使用node.js制作代理服务器,图文并茂,十分的详细,代码很简洁易懂,这里推荐给大家。

  6. node.js中的fs.openSync方法使用说明

    这篇文章主要介绍了node.js中的fs.openSync方法使用说明,本文介绍了fs.openSync方法说明、语法、接收参数、使用实例和实现源码,需要的朋友可以参考下

  7. Node.js+ELK日志规范的实现

    这篇文章主要介绍了Node.js+ELK日志规范的实现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  8. node.js爬虫框架node-crawler初体验

    这篇文章主要介绍了node.js爬虫框架node-crawler的相关资料,帮助大家利用node.js进行爬虫,感兴趣的朋友可以了解下

  9. node.js中的fs.existsSync方法使用说明

    这篇文章主要介绍了node.js中的fs.existsSync方法使用说明,本文介绍了fs.existsSync方法说明、语法、接收参数、使用实例和实现源码,需要的朋友可以参考下

  10. 说说如何利用 Node.js 代理解决跨域问题

    这篇文章主要介绍了Node.js代理解决跨域问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

随机推荐

  1. Error: Cannot find module ‘node:util‘问题解决

    控制台 安装 Vue-Cli 最后一步出现 Error: Cannot find module 'node:util' 问题解决方案1.问题C:\Windows\System32>cnpm install -g @vue/cli@4.0.3internal/modules/cjs/loader.js:638 throw err; &nbs

  2. yarn的安装和使用(全网最详细)

    一、yarn的简介:Yarn是facebook发布的一款取代npm的包管理工具。二、yarn的特点:速度超快。Yarn 缓存了每个下载过的包,所以再次使用时无需重复下载。 同时利用并行下载以最大化资源利用率,因此安装速度更快。超级安全。在执行代码之前,Yarn 会通过算法校验每个安装包的完整性。超级可靠。使用详细、简洁的锁文件格式和明确的安装算法,Yarn 能够保证在不同系统上无差异的工作。三、y

  3. 前端环境 本机可切换node多版本 问题源头是node使用的高版本

    前言投降投降 重头再来 重装环境 也就分分钟的事 偏要折腾 这下好了1天了 还没折腾出来问题的源头是node 使用的高版本 方案那就用 本机可切换多版本最终问题是因为nodejs的版本太高,导致的node-sass不兼容问题,我的node是v16.14.0的版本,项目中用了"node-sass": "^4.7.2"版本,无法匹配当前的node版本根据文章的提

  4. nodejs模块学习之connect解析

    这篇文章主要介绍了nodejs模块学习之connect解析,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  5. nodejs npm package.json中文文档

    这篇文章主要介绍了nodejs npm package.json中文文档,本文档中描述的很多行为都受npm-config(7)的影响,需要的朋友可以参考下

  6. 详解koa2学习中使用 async 、await、promise解决异步的问题

    这篇文章主要介绍了详解koa2学习中使用 async 、await、promise解决异步的问题,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  7. Node.js编写爬虫的基本思路及抓取百度图片的实例分享

    这篇文章主要介绍了Node.js编写爬虫的基本思路及抓取百度图片的实例分享,其中作者提到了需要特别注意GBK转码的转码问题,需要的朋友可以参考下

  8. CentOS 8.2服务器上安装最新版Node.js的方法

    这篇文章主要介绍了CentOS 8.2服务器上安装最新版Node.js的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  9. node.js三个步骤实现一个服务器及Express包使用

    这篇文章主要介绍了node.js三个步骤实现一个服务器及Express包使用,文章通过新建一个文件展开全文内容,具有一定的参考价值,需要的小伙伴可以参考一下

  10. node下使用UglifyJS压缩合并JS文件的方法

    下面小编就为大家分享一篇node下使用UglifyJS压缩合并JS文件的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

返回
顶部