Node.js 利用多进程优化 CPU 密集型计算
Node.js 单线程模型在处理长时间运算时易导致 CPU 阻塞,不适合 CPU 密集型应用。本文通过斐波那契数列计算示例,对比单线程与基于 Cluster 模块的多进程方案。结果显示,利用多核 CPU 创建多个子进程并行处理,可将执行时间从 44 秒缩短至 10 秒左右,显著提升处理效率。
Node.js 单线程与多进程
Node.js 单线程与多进程
Node.js 凭借异步事件驱动和非阻塞 I/O 机制,在性能方面表现优异,因此被广泛采用。然而,其单线程架构也存在明显局限:当执行长时间运算时,CPU 无法及时释放,导致程序响应变慢,因此 Node.js 并非处理 CPU 密集型任务的最佳选择。
尽管 Node.js 不支持多线程,但可以通过创建多个子进程来分担任务。Node.js 内置了 child_process 和 cluster 两个模块,可用于实现多子进程并发执行。
单线程与多进程性能对比测试
接下来,我们将分别使用单线程和多进程两种方式,模拟大量斐波那契数的查找计算,以此进行 CPU 密集型测试。
以下代码用于查找第 35 位的斐波那契数,共执行 500 次。选择该位置是为了确保测试时间既不会过短导致误差,也不会过长影响体验。
单线程处理
代码文件: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 查看运行结果。
在我的电脑配置下,运行结果显示耗时为 44611 毫秒(具体数值因电脑配置不同而异)。
... process: 500/500 👏 👏 👏 👏 👏 👏 👏 👏 👏 👏 任务完成,用时: 44611ms 👏 👏 👏 👏 👏 👏 👏 👏 👏 👏
查找 500 次耗时约 44 秒,速度较慢。可以预见,如果计算位置更大或任务数量更多,耗时将成倍增加。
接下来我们尝试使用多进程方案进行优化 ⬇️
基于 Cluster 模块的多进程实现
多进程
采用 cluster 模块,基于 Master-Worker 模式进行测试。
项目包含 3 个 JS 文件:主线程代码 master.js、子进程代码 worker.js 以及入口代码 cluster.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 查看运行结果。
在我的电脑配置下,运行结果显示耗时为 10724 毫秒(具体数值因电脑配置不同而异)。
process: 500/500 👏 👏 👏 👏 👏 👏 👏 👏 👏 👏 任务完成,用时: 10724ms 👏 👏 👏 👏 👏 👏 👏 👏 👏 👏
结果
通过上述两种方式的对比,结果显而易见:多进程处理速度是单线程的 4 倍多。此外,在硬件条件允许且 CPU 核心数充足的情况下,增加进程数量可以进一步提升处理速度。
如果有更优的方案或其他语言能更好地满足需求,那自然是更好的选择。毕竟 Node.js 天生就不太适合 CPU 密集型应用。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
怎么在本地运行JavaScript脚本:Node.js下载安装与环境配置教程
2026-11-05 09:02:33
JavaScript开发环境怎么搭建:Node.js安装与VS Code配置指南
2026-11-03 23:36:59
JavaScript零基础入门:环境搭建与第一个Hello World
2026-11-03 23:34:09
NW.js 是什么:基于 Chromium 和 Node.js 的桌面应用框架
2026-11-03 15:14:00
nvm、n 和官方安装包:哪种 Node.js 升级方式更适合你
2026-11-03 14:54:28
