限制 Node.js 中循环中产生的并发子进程数
Limit the number of concurrent child processes spawned in a loop in Node.js
我正在尝试使用 child_process.spawn
在 for 循环中调用 CLI 工具,每次调用时使用不同的参数。到目前为止一切顺利,但是如果我想引入最大数量的子进程并且只在前一个进程关闭时继续生成新进程,我 运行 就会遇到麻烦。当达到有限的子进程数量时,我想用无限 while 循环停止 for 循环。但是,子进程似乎永远不会触发 'close' 事件。
以ls
为例(抱歉,我想不出一个好的、持久的命令,可以在一段时间后自动退出):
const { spawn } = require("child_process");
const max = 3;
let current = 0;
// dirsToVisit is an array of paths
for (let i = 0; i < dirsToVisit.length; i++) {
// if already running 3 ls, wait till one closes
while (current >= max) {}
current++;
lsCommand(dirsToVisit[i]);
}
function lsCommand(dir) {
const ls = spawn("ls", [dir]);
ls.on("close", code => {
current--;
console.log(`Finished with code ${code}`);
});
}
上面的代码永远不会退出,当子进程退出时要在控制台中记录的字符串永远不会打印在屏幕上。如果我删除 while 循环,所有子进程最终都会毫无问题地完成,但是同时允许的进程数量没有限制。
为什么我的代码不起作用,如何正确限制循环中生成的子进程数?如有任何帮助,我们将不胜感激!
您的代码不起作用,因为 lsCommand()
是非阻塞的异步代码。它所做的只是启动生成操作,然后立即 return。因此,您的 for
循环从 运行 开始,然后您的 while
在 for
循环的第一次迭代中循环 运行 并开始最大 lsCommand()
调用然后退出。 for
循环的后续迭代无事可做,因为 max
lsCommand()
调用已经 运行ning。因此,由于 lsCommand()
是非阻塞的,因此您的 for
循环结束,它所做的只是开始 max
lsCommand()
操作,然后您的循环就完成了。您需要做的是,您必须注意每个 lsCommand() by monitoring
ls.on('close')` 的完成,然后当每个完成时,您可以开始另一个。您可以在下面的代码中看到我是如何做到这一点的。
你可以做这样的事情,你可以创建一个内部函数,它有一个循环来启动进程达到你的限制,然后你只需在每次生成操作完成时继续调用该函数(这将再次启动一个每次完成):
function listDirs(dirsToVisit, maxAtOnce) {
let numRunning = 0;
let index = 0;
function runMore() {
// while we need to start more, start more of them
while (numRunning < maxAtOnce && index < dirsToVisit.length) {
++numRunning;
const ls = spawn("ls", [dirsToVisit[index++]]);
ls.on("close", code => {
--numRunning;
console.log(`Finished with code ${code}`);
runMore();
}).on("error", err => {
--numRunning;
runMore();
});
}
if (numRunning === 0) {
// all done with all requests here
}
}
runMore();
}
对于一些更通用的实现,请参见这些:
Nodejs: Async request with a list of URL
我正在尝试使用 child_process.spawn
在 for 循环中调用 CLI 工具,每次调用时使用不同的参数。到目前为止一切顺利,但是如果我想引入最大数量的子进程并且只在前一个进程关闭时继续生成新进程,我 运行 就会遇到麻烦。当达到有限的子进程数量时,我想用无限 while 循环停止 for 循环。但是,子进程似乎永远不会触发 'close' 事件。
以ls
为例(抱歉,我想不出一个好的、持久的命令,可以在一段时间后自动退出):
const { spawn } = require("child_process");
const max = 3;
let current = 0;
// dirsToVisit is an array of paths
for (let i = 0; i < dirsToVisit.length; i++) {
// if already running 3 ls, wait till one closes
while (current >= max) {}
current++;
lsCommand(dirsToVisit[i]);
}
function lsCommand(dir) {
const ls = spawn("ls", [dir]);
ls.on("close", code => {
current--;
console.log(`Finished with code ${code}`);
});
}
上面的代码永远不会退出,当子进程退出时要在控制台中记录的字符串永远不会打印在屏幕上。如果我删除 while 循环,所有子进程最终都会毫无问题地完成,但是同时允许的进程数量没有限制。
为什么我的代码不起作用,如何正确限制循环中生成的子进程数?如有任何帮助,我们将不胜感激!
您的代码不起作用,因为 lsCommand()
是非阻塞的异步代码。它所做的只是启动生成操作,然后立即 return。因此,您的 for
循环从 运行 开始,然后您的 while
在 for
循环的第一次迭代中循环 运行 并开始最大 lsCommand()
调用然后退出。 for
循环的后续迭代无事可做,因为 max
lsCommand()
调用已经 运行ning。因此,由于 lsCommand()
是非阻塞的,因此您的 for
循环结束,它所做的只是开始 max
lsCommand()
操作,然后您的循环就完成了。您需要做的是,您必须注意每个 lsCommand() by monitoring
ls.on('close')` 的完成,然后当每个完成时,您可以开始另一个。您可以在下面的代码中看到我是如何做到这一点的。
你可以做这样的事情,你可以创建一个内部函数,它有一个循环来启动进程达到你的限制,然后你只需在每次生成操作完成时继续调用该函数(这将再次启动一个每次完成):
function listDirs(dirsToVisit, maxAtOnce) {
let numRunning = 0;
let index = 0;
function runMore() {
// while we need to start more, start more of them
while (numRunning < maxAtOnce && index < dirsToVisit.length) {
++numRunning;
const ls = spawn("ls", [dirsToVisit[index++]]);
ls.on("close", code => {
--numRunning;
console.log(`Finished with code ${code}`);
runMore();
}).on("error", err => {
--numRunning;
runMore();
});
}
if (numRunning === 0) {
// all done with all requests here
}
}
runMore();
}
对于一些更通用的实现,请参见这些:
Nodejs: Async request with a list of URL