web-dev-qa-db-ja.com

電子キルchild_process.exec

child_process.execを使用して長時間実行されるタスクを実行する電子アプリがあります。これらのタスク中にユーザーがアプリを終了するタイミングを管理するのに苦労しています。

それらが私のアプリを終了するか、閉じるを押すと、子プロセスは終了するまで実行を続けますが、電子アプリウィンドウはすでに閉じて終了しています。

プロセスがまだ実行中であることをユーザーに通知し、プロセスが終了したらアプリウィンドウを閉じる方法はありますか?

main.jsにあるのは、標準コードだけです。

// Quit when all windows are closed.
app.on('window-all-closed', function() {
    // On OS X it is common for applications and their menu bar
    // to stay active until the user quits explicitly with Cmd + Q
    if (process.platform != 'darwin') {
        app.quit();
    }
});

どこかに小切手を追加する必要がありますか?

ご協力いただきありがとうございます

編集済み

child_processのPIDは、完了するまで取得できないようです。これは私のchild_processコードです

var loader = child_process.exec(cmd, function(error, stdout, stderr) {
    console.log(loader.pid)
    if (error) {
        console.log(error.message);
    }
    console.log('Loaded: ', value);
});

別の方法で取得しようとすべきですか?

12
tjmgis

それで、みんなの素晴らしいコメントの後、私はそれを機能させるためにいくつかの追加で私のコードを更新することができたので、他のみんなのために私の更新を投稿しています。

1)child_process.execからchild_process.spawnに変更します

var loader = child_process.spawn('program', options, { detached: true })

2)Electron ipcRendererを使用して、モジュールからmain.jsスクリプトに通信します。これにより、PIDをmain.jsに送信できます

ipcRenderer.send('pid-message', loader.pid);

ipcMain.on('pid-message', function(event, arg) {
  console.log('Main:', arg);
  pids.Push(arg);
});

3)それらのPIDをアレイに追加します

4)main.jsに次のコードを追加して、アプリを終了する前に配列に存在するPIDをすべて強制終了します。

// App close handler
app.on('before-quit', function() {
  pids.forEach(function(pid) {
    // A simple pid lookup
    ps.kill( pid, function( err ) {
        if (err) {
            throw new Error( err );
        }
        else {
            console.log( 'Process %s has been killed!', pid );
        }
    });
  });
});

みんなの助けをありがとう。

14
tjmgis

ChildProcessは、プロセスが終了するとexitイベントを発行します。配列内の現在のプロセスを追跡し、exitイベントが発生した後、それらを削除する場合は、アプリを終了すると、ChildProcess.kill()を実行している残りの配列をforeachだけ実行できるはずです。

私は今それをテストする立場にないので、これは100%動作するコードではないかもしれません/物事を行うための最良の方法ではありませんが、正しい道を歩むには十分なはずです。

_var processes = [];

// Adding a process
var newProcess = child_process.exec("mycommand");
processes.Push(newProcess);
newProcess.on("exit", function () {
  processes.splice(processes.indexOf(newProcess), 1);
});

// App close handler
app.on('window-all-closed', function() {
  if (process.platform != 'darwin') {
    processes.forEach(function(proc) {
      proc.kill();
    });

    app.quit();
  }
});
_

EDIT: shreikがコメントで述べたように、ChildProcessオブジェクトの代わりにPIDを配列に格納し、process.kill(pid)を使用してそれらを強制終了することもできます。もう少し効率的かもしれません!

4
Joe Clay

別の解決策exec()を使い続けたい場合

exec()によって実行されている子プロセスを強制終了するには、モジュール _ps-tree_ を調べてください。彼らは何が起こっているのかを説明します。

uNIXでは、プロセスはexit呼び出しを使用して終了する場合があり、その親プロセスは、waitシステム呼び出しを使用してそのイベントを待機する場合があります。待機システムコールは、終了した子のプロセスIDを返すため、親は、おそらく多くの子のどれが終了したかを通知します。ただし、親が終了した場合、そのすべての子は新しい親としてinitプロセスを割り当てます。したがって、子には、ステータスと実行統計を収集するための親がまだあります。 (「オペレーティングシステムの概念」から)

解決策:_ps-tree_を使用して、_child_process_が開始した可能性のあるすべてのプロセスを取得します。

exec()は実際には次のように機能します:

_function exec (cmd, cb) {
  spawn('sh', ['-c', cmd]);
  ...
}
_

例を確認して、ニーズに合わせて調整してください

_var cp = require('child_process'),
psTree = require('ps-tree');

var child = cp.exec("node -e 'while (true);'", function () { /*...*/ });

psTree(child.pid, function (err, children) {
    cp.spawn('kill', ['-9'].concat(children.map(function (p) { return p.PID })));
});
_
1
ChesuCR