【问题标题】:Limit the number of concurrent child processes spawned in a loop in Node.js限制 Node.js 循环中产生的并发子进程的数量
【发布时间】:2019-04-08 05:20:53
【问题描述】:

我正在尝试使用 child_process.spawn 在 for 循环中调用 CLI 工具,每次调用时使用不同的参数。到目前为止一切都很好,但是如果我想引入最大数量的子进程并且只在前一个进程关闭时继续生成新进程,我就会遇到麻烦。当达到有限的子进程数量时,我想用无限的while循环停止for循环。但是,子进程似乎从不触发“关闭”事件。

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 循环,所有子进程最终都会顺利完成,但同时允许的进程数没有限制。

为什么我的代码不工作,如何正确限制循环中产生的子进程的数量?任何帮助将不胜感激!

【问题讨论】:

    标签: javascript node.js concurrency child-process


    【解决方案1】:

    您的代码不起作用,因为lsCommand() 是非阻塞、异步的。它所做的只是启动生成操作,然后立即返回。因此,您的for 循环开始运行,然后您的while 循环在for 循环的第一次迭代中运行并开始最大lsCommand() 调用,然后它退出。 for 循环的后续迭代无事可做,因为 max lsCommand() 调用已经在运行。所以,由于lsCommand() 是非阻塞的,你的for 循环结束,它所做的只是启动maxlsCommand() 操作,然后你的循环就完成了。你要做的是你必须观察每个lsCommand() by monitoringls.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();
    }
    

    对于一些更通用的实现,请参阅这些:

    Loop through an api get request with variable URL

    Promise.all consumes all my RAM

    Javascript - how to control how many promises access network in parallel

    Nodejs: Async request with a list of URL

    【讨论】:

    • 感谢您的建议,稍后会尝试!您知道为什么我的代码不起作用吗? :)
    • @John - 我在答案的开头添加了一个段落,解释了为什么您的代码无法正常工作。
    猜你喜欢
    • 1970-01-01
    • 2014-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-17
    • 2020-08-29
    • 1970-01-01
    相关资源
    最近更新 更多