Ограничить число параллельных дочерних процессов, порожденных в цикле в Node.js - PullRequest
0 голосов
/ 04 ноября 2018

Я пытаюсь использовать child_process.spawn для вызова инструмента CLI в цикле for с разными аргументами при каждом вызове. Пока все хорошо, но если я хочу ввести максимальное количество дочерних процессов и продолжать порождать новые процессы только после закрытия предыдущего процесса, у меня возникают проблемы. Я хотел остановить цикл for бесконечным циклом while, когда достигается ограниченное количество дочерних процессов. Однако дочерние процессы, кажется, никогда не запускают событие '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, все дочерние процессы завершатся в конце без проблем, но не будет ограничений на количество процессов, разрешенных одновременно.

Почему мой код не работает и как правильно ограничить количество дочерних процессов, порождаемых в цикле? Любая помощь будет оценена!

1 Ответ

0 голосов
/ 04 ноября 2018

Ваш код не работает, потому что lsCommand() неблокирующий, асинхронный. Все, что он делает, это запускает операции вызова и затем сразу же возвращается. Итак, ваш цикл for начинает работать, затем ваши циклы while запускаются на первой итерации цикла for и запускают вызовы max lsCommand(), а затем завершаются. Последующим итерациям цикла for больше нечего делать, поскольку вызовы max lsCommand() уже выполняются. Итак, поскольку 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();
}

Для некоторых более общих реализаций, смотрите эти:

Цикл запроса API с переменным URL

Promise.all потребляет всю мою оперативную память

Javascript - как контролировать количество обещаний доступа к сети параллельно

Nodejs: асинхронный запрос со списком URL

...