【发布时间】:2016-10-28 13:42:20
【问题描述】:
当使用一个子进程的标准输出作为另一个子进程的标准输入时,似乎有时数据不会传递给下一个子进程:
var spawn = require('child_process').spawn;
var pipeId = 0;
var launchProcess = function(cmd, args, stdin){
return spawn(cmd, args, {
stdio: [stdin ? stdin : 'ignore', 'pipe', 'pipe']
});
};
var launch = function(){
var task0 = launchProcess('echo', ['how\nare\nyou\ndear\nstranger']);
var task1 = launchProcess('tee', ['/tmp/body-pipeline-' + pipeId], task0.stdout);
pipeId++;
task1.on('exit', launch);
};
launch();
有些文件是空的:
ls -lhS /tmp/body-pipeline-*
我还尝试通过访问task0.stdout._handle.fd 将文件描述符作为正整数传递,但问题仍然存在。
据我所知,这就是 shell 管道的工作方式:一个进程的标准输出的相同文件描述符被用作另一个进程的标准输入。我试图避免通过 NodeJS 进程传递所有数据,因为当子进程输出大量数据时,它会导致 CPU 负载过高。
更新:当管道同时用于标准输入和标准输出时,一切都按预期工作(在此处使用 cat 测试更长的文本):
var spawn = require('child_process').spawn;
var pipeId = 0;
var launchProcess = function(cmd, args, stdin){
return spawn(cmd, args, {
stdio: [stdin ? stdin : 'pipe', 'pipe', 'pipe']
});
};
var launch = function(){
var task0 = launchProcess('cat');
var task1 = launchProcess('tee', ['/tmp/body-pipeline-' + pipeId]);
task0.stdout.pipe(task1.stdin)
task0.stdin.write(JSON.stringify(process.env).split(',').join('\n'))
task0.stdin.end();
pipeId++;
task1.on('exit', launch);
};
launch();
Update2:当使用task0.stdout.pipe(task1.stdin) 时,脚本使用 50% CPU(相比之下,将 task0 的标准输出作为 task1 的标准输入时为 0%):
var spawn = require('child_process').spawn;
var pipeId = 0;
var launchProcess = function(cmd, args, stdin, stdout, stderr){
return spawn(cmd, args, {
stdio: [stdin, stdout, stderr]
});
};
var launch = function(){
var task0 = launchProcess('yes', ['lala'], 'ignore', 'pipe', 'ignore');
var task1 = launchProcess('tee', ['/tmp/body-pipeline-' + pipeId], 'pipe', 'ignore', 'ignore');
// var task1 = launchProcess('tee', ['/tmp/body-pipeline-' + pipeId], task0.stdout, 'ignore', 'ignore');
task0.stdout.pipe(task1.stdin);
pipeId++;
task1.on('exit', launch);
};
launch();
Update3:这更好地说明了我的问题。我试图在原始代码中简化它,但我认为它过于简化了。 Larry Turtis 为简化案例提供了一种解决方法,但不适用于我的情况:
var spawn = require('child_process').spawn;
var pipeId = 0;
var pipeSlots = 6;
var launchProcess = function(cmd, args, stdin, stdout){
return spawn(cmd, args, {
stdio: [stdin, stdout, 'ignore']
});
};
var launch = function(){
var task0 = launchProcess('echo', ['how\nare\nyou\ndear\nstranger'], 'ignore', 'pipe');
var task1 = launchProcess('tee', ['/tmp/body-pipeline-' + pipeId], task0.stdout, 'ignore');
task0.on('error', function(err){
console.log('Error while processing task0:' + err.stack);
});
task1.on('error', function(err){
console.log('Error while processing task1:' + err.stack);
});
pipeId++;
};
// Simulating message queue
setInterval(function(){
// Simulating how many messages we get from the messaging queue
var mqMessageCount = Math.floor(Math.random() * (pipeSlots + 1));
for(var i = 0; i < mqMessageCount; i++){
launch();
}
}, 250); // For this test we assume that pipes finish under 250ms
【问题讨论】:
-
提示:您应该几乎总是使用子进程的
'close'事件而不是'exit',因为前者表明stdout/stderr 上没有更多数据可用。'close'总是在'exit'之后。 -
@mscdex 感谢您提供的信息!派生一个新进程不应有不同的行为,具体取决于其他派生进程是正在运行还是已完成。
标签: node.js pipe race-condition file-descriptor child-process