Запуск и остановка дочерних процессов из основного процесса только при необходимости в nodejs

Я как-то новичок в nodejs и дочерних процессах, и теперь у меня возникают проблемы в моем приложении. Кое-что о фоне: у меня работает nodejs-Websocket-Server, который загружает HTML-страницу любому пользователю, который подключается. Если пользователь нажимает кнопку в HTML, он отправляет сообщение на сервер о том, что он должен запустить процесс захвата изображения (как дочерний процесс в узле). Таким образом, вы можете сравнить это с каким-то бесконечным циклом, который выполняется в дочернем элементе. Пока все хорошо, это работает для меня.

Но теперь, если пользователь снова нажмет кнопку, дочерний процесс должен быть остановлен, поэтому захват изображения не пойдет дальше. Моя проблема в том, что я понятия не имею, как остановить дочерний процесс из основного процесса, не убивая его полностью (пользователь должен иметь возможность снова запустить цикл).

Это фрагмент основного процесса («старт» имеет значение true или false, в зависимости от того, нажал ли пользователь кнопку запуска. Каждый раз, когда пользователь нажимает кнопку, «старт» меняет свое состояние):

var aCapLive = child.fork(__dirname + "/cp_capture_liveimg_async.js");  //Livebild
if (start) aCapLive.send(start);
else aCapLive.send(start);

А это от ребенка:

var i = 0;
process.on("message", function(message)
{
    while(message)
    {
        console.log("CP aCapLive received message: Iteration " + i++);
    }
    console.log("CP aCapLive is going to exit now...");
    process.exit();
}

Очевидно, это не работает для меня, потому что я работаю в своем цикле и не могу обнаружить никаких изменений в «сообщении». Буду очень признателен, если у кого-то есть идеи :)


person Fidel90    schedule 12.07.2013    source источник


Ответы (2)


Ваш код в том виде, в каком он у вас есть, никогда не получит более одного сообщения, потому что он немедленно переходит в бесконечный цикл, который никогда не возвращает управление циклу обработки событий. Переменная message никогда не изменится волшебным образом, и, поскольку узел является однопоточным, у дочернего элемента нет возможности выйти, если только самое первое сообщение не будет оценено как ложное.

Я думаю, вы хотите что-то еще вроде этого:

var i = 0;
process.on("message", function(message)
{
    if(message)
    {
        console.log("CP aCapLive received message: Iteration " + i++);
    }
    else
    {
        console.log("CP aCapLive is going to exit now...");
        process.exit();
    }
});

Если вам нужен бесконечный цикл, вы должны возвращать управление циклу событий после каждой итерации. Это даст возможность поступать новым сообщениям. Пример:

var _message;
var _running = false;
process.on("message", function (message)
{
    _message = message; // update the _message var when a new one comes in
    if (!_running) // this just prevents multiple loops from being started.
    {
        _running = true;
        run();
    }
});

var i = 0;
function run ()
{
    if(_message)
    {
        console.log("CP aCapLive received message: Iteration " + i++);
        setImmediate(run); // <-- this causes an infinite non-blocking loop
    }
    else
    {
        console.log("CP aCapLive is going to exit now...");
        process.exit();
    }
}

Если message будет одинаковым каждый раз, вам действительно нужна система переключения как таковая:

var _message = false;
process.on("message", function ()
{
    _message = !_message; // update the _message var when a new one comes in
    if (_message)
        run();
});

var i = 0;
function run ()
{
    if(_message)
    {
        i++;
        if (i % 10000 === 0)
            console.log("CP aCapLive received message: Iteration " + i);
        setImmediate(run); // <-- this causes an infinite non-blocking loop
    }
    else
    {
        console.log("CP aCapLive is going to exit now...");
        process.exit();
    }
}

Вот код родительского процесса, который я использовал для его проверки:

var childProcess = require('child_process');

var proc = childProcess.fork('child.js');
proc.send(true);
setTimeout(function () { proc.send(true); }, 4000); // send second message

Выход:

CP aCapLive received message: Iteration 10000
CP aCapLive received message: Iteration 20000
CP aCapLive received message: Iteration 30000

... full output omitted for brevity ...

CP aCapLive received message: Iteration 1660000
CP aCapLive received message: Iteration 1670000
CP aCapLive received message: Iteration 1680000
CP aCapLive is going to exit now...
person Bret Copeland    schedule 12.07.2013
comment
Ну, да, у меня было такое раньше, но проблема в том, что я ХОЧУ выполнить бесконечный цикл, когда сообщение истинно. Когда пользователь снова нажимает кнопку, сообщение должно измениться на false, и цикл должен остановиться. Может быть, у меня есть неправильная идея сделать что-то. вот так, но только так я себе это и представляю ;) - person Fidel90; 12.07.2013
comment
Хорошо, есть способ сделать это, но он сложнее... вот, я напишу. - person Bret Copeland; 12.07.2013
comment
Теперь я получаю следующую ошибку в консоли: (узел) предупреждение: обнаружен рекурсивный процесс.nextTick. Это сломается в следующей версии node. Пожалуйста, используйте setImmediate для рекурсивной отсрочки. RangeError: превышен максимальный размер стека вызовов - person Fidel90; 13.07.2013
comment
Извините, тогда просто замените process.nextTick() на setImmediate(). SetImmediate по-прежнему пропускает ввод-вывод, поэтому он должен работать. - person Bret Copeland; 13.07.2013
comment
Хм, я сделал это, и мой цикл работает, работает и работает, но никогда не останавливается повторным нажатием кнопки. :( - person Fidel90; 14.07.2013
comment
Я проверил, что код, который я разместил, работает. Возможно, ваша проблема в том, что когда вы снова нажимаете кнопку, отправленное message оценивается как истинное и, следовательно, не прерывает цикл. Если это так, вам либо нужно убедиться, что при повторном нажатии message == false, либо вам нужно настроить систему переключения, и я добавил пример для этого. - person Bret Copeland; 14.07.2013
comment
Привет, Брет, спасибо за помощь! Но я не знаю, что здесь происходит не так... Я попробовал вашу систему переключения (и мне нравится идея), но она тоже не работает для меня :( В вашем последнем примере, когда я снова нажимаю кнопку, цикл не прерывается. но начните еще один. Не знаю, что идет не так... кажется, я не могу по какой-то причине разорвать цикл. Я просто скопировал у вас пример, чтобы избежать опечаток, но я не могу заставить его работать.. - person Fidel90; 14.07.2013
comment
Я добавил полный код, который использовал для его тестирования, и вывод. Это определенно сработает, если второе сообщение будет отправлено правильно. Если вас беспокоит, будет ли когда-либо получено второе сообщение, поместите console.log в обратный вызов process.on("message". Также убедитесь, что в вашем коде нет бесконечного цикла блокировки. - person Bret Copeland; 14.07.2013
comment
Ах, идиот я! Извини, Брет, за то, что у меня так много проблем. У меня была простая ошибка в моем основном процессе, и я понял это только сейчас. Я разветвлял дочерний процесс каждый раз, когда нажимал кнопку. Я исправил это, и теперь я только разветвляю дочерний процесс в верхней части основного процесса. Теперь даже ваш второй пример кода отлично работает для меня. Цикл проходит примерно на 1-2 итерации дальше после нажатия кнопки, чтобы остановить его, но затем он останавливается должным образом. Я вам очень-очень благодарна, вы мне очень помогли. Желаю тебе хорошего дня! :) - person Fidel90; 14.07.2013
comment
Рад, что смог помочь. Добро пожаловать в StackOverflow. - person Bret Copeland; 14.07.2013

Я исправил некоторые ошибки, так что теперь я могу запускать и останавливать цикл так часто, как захочу (заменив process.exit() на clearImmediate(id), чтобы процесс можно было выполнять снова и снова):

var _message;
var _running = false;
var i = 0;

process.on("message", function (start)
{
    _message = start; // update the _message var when a new one comes in
    if (!_running) // this just prevents multiple loops from being started.
    {
        _running = true;
        run();
    }
});


function run ()
{
    if(_message)
    {
        console.log("CP aCapLive received message: Iteration " + i++);
        var id = setImmediate(run); // <-- this causes an infinite non-blocking loop
    }
    else
    {
        console.log("CP aCapLive is going to exit now...");
        _running = false;
        clearImmediate(id);
    }
}
person Fidel90    schedule 14.07.2013