child_process spawn在nodejs中出现竞争条件

时间:2015-02-17 22:56:55

标签: node.js race-condition fs

我开始学习和使用节点,我喜欢它,但我不确定某些功能是如何工作的。也许你可以帮我解决一个这样的问题:

我希望在休息命令时从我的节点服务器生成本地脚本和程序。看看fs库我看到下面的示例如何生成子进程并在其上添加一些管道/事件处理程序。

var spawn = require('child_process').spawn,
    ps    = spawn('ps', ['ax']),
    grep  = spawn('grep', ['ssh']);

ps.stdout.on('data', function (data) {
  grep.stdin.write(data);
});

ps.stderr.on('data', function (data) {
  console.log('ps stderr: ' + data);
});

ps.on('close', function (code) {
  if (code !== 0) {
    console.log('ps process exited with code ' + code);
  }
  grep.stdin.end();
});

grep.stdout.on('data', function (data) {
  console.log('' + data);
});

grep.stderr.on('data', function (data) {
  console.log('grep stderr: ' + data);
});

grep.on('close', function (code) {
  if (code !== 0) {
    console.log('grep process exited with code ' + code);
  }
});

对我来说奇怪的是,我不明白如何保证在程序开始运行之前注册事件处理程序代码。它并不像那里的“简历”那样。你跑来启动孩子的功能。这不是竞争条件吗?当然,条件将微不足道,并且几乎从未打过,因为它之后会有如此短暂的代码剪切,但如果是这样的话,我宁愿不按照良好习惯进行编码。

所以: 1)如果不是比赛条件,为什么? 2)如果是竞争条件我怎么能以正确的方式写出来?

谢谢你的时间!

2 个答案:

答案 0 :(得分:1)

这不是竞争条件。 Node.js是单线程的,以先到先得的方式处理事件。在事件循环结束时放置新事件。 Node将以同步方式执行您的代码,其中一部分将涉及设置事件发射器。当这些事件发射器发出事件时,它们将被放到队列的末尾,并且在Node完成执行其当前正在处理的任何代码片段之前将不会被处理,这恰好是注册侦听器的相同代码。因此,在处理事件之前,将始终注册侦听器。

答案 1 :(得分:0)

鉴于已接受答案的评论中存在轻微冲突和含糊不清,下面的示例和输出告诉我两件事:

  1. 即使真正的基础进程正在运行/执行,子进程(指的是spawn返回的节点对象)也不会发出事件。
  2. 在执行子进程之前,已设置IPC的管道。

两者都很明显。冲突是w.r.t. OP的问题的解释:-

实际上是“是”,如果需要考虑实际子进程的副作用,这就是数据争用情况的缩影。但是“不”,就IPC管道管道而言,没有数据争用。当上下文完成时(如前所述),数据将被写入缓冲区并作为(更大)blob进行检索。

下面看到的第一个数据事件在我们阻塞时不会被子进程推送1个而是将5个块推入stdout。因此没有丢失。

样本:

let t = () => (new Date()).toTimeString().split(' ')[0]
let p = new Promise(function (resolve, reject) {
  console.log(`[${t()}|info] spawning`);

  let cp = spawn('bash', ['-c', 'for x in `seq 1 1 10`; do printf "$x\n"; sleep 1; done']);
  let resolved = false;

  if (cp === undefined)
    reject();

  cp.on('error', (err) => {
    console.log(`error: ${err}`);
    reject(err);
  });

  cp.stdout.on('data', (data) => {
    if (!resolved) {
      console.log(`[${t()}|info] spawn succeeded`);
      resolved = true;
      resolve();
    }
    process.stdout.write(`[${t()}|data] ${data}`);
  });

  let ts = parseInt(Date.now() / 1000);
  while (parseInt(Date.now() / 1000) - ts < 5) {
    // waste some cycles in the current context
    ts--; ts++;
  }

  console.log(`[${t()}|info] synchronous time wasted`);
});
Promise.resolve(p);

输出:

[18:54:18|info] spawning
[18:54:23|info] synchronous time wasted
[18:54:23|info] spawn succeeded
[18:54:23|data] 1
2
3
4
5
[18:54:23|data] 6
[18:54:24|data] 7
[18:54:25|data] 8
[18:54:26|data] 9
[18:54:27|data] 10