为什么要有Node子进程
node遵循的是单线程单进程的模式,node的单线程是指js的引擎只有一个实例,且在nodejs的主线程中执行。node的单线程使得在主线程不能进行cpu密集操作,否则会阻塞主线程。对于cpu密集型操作,在node中通过child_process可以创建独立的子进程。父子进程通过IPC通信,子进程执行后可以将结果返回给父进程。
node是严格单线程的吗
node不是严格的单线程,node中存在多个线程
js引擎执行的线程
定时器线程(setTimeout, setInterval)
异步http线程(ajax)
我们平时所说的单线程是指node中只有一个js引擎在主线程上运行。其他异步IO和事件驱动相关的线程通过libuv来实现内部的线程池和线程调度。libv中存在了一个Event Loop,通过Event Loop来切换实现类似于多线程的效果。简单的来讲Event Loop 就是维持一个执行栈和一个时间队列,当前执行栈中的如果发现异步IO以及定时器等函数,就会把这些异步会掉函数放入到事件队列中。当前执行栈执行完成后,从事件队列中,按照一定的顺序执行事件队列中的异步回调函数。
![](https://img.haomeiwen.com/i5476585/28d821d6e045ff43.png)
创建node子进程的四种方式
有四种方法创建node子进程,分别是spawn,execFile,exec,fork
![](https://img.haomeiwen.com/i5476585/224630a826e5666e.png)
上图可以展示出这4个方法的区别,我们也可以简要介绍这4中方法的不同。
spawn : 子进程中执行的是非node程序,提供一组参数后,执行的结果以流的形式返回。
execFile:子进程中执行的是非node程序,提供一组参数后,执行的结果以回调的形式返回。
exec:子进程执行的是非node程序,传入一串shell命令,执行后结果以回调的形式返回,与execFile不同的是exec可以直接执行一串shell命令。
fork:子进程执行的是node程序,提供一组参数后,执行的结果以流的形式返回,与spawn不同,fork生成的子进程只能执行node应用。
使用spwan创建子进程
使用child_process.spawn创建一个子进程,会返回一个带有stdout和stderr流的对象。你可以通过stdout流来读取子进程返回给Node.js的数据。stdout拥有’data’,’end’以及一般流所具有的事件。当想要子进程返回大量数据给Node时,比如说图像处理,读取二进制数据等等,最好使用spawn方法。
![](https://img.haomeiwen.com/i5476585/de8e74f7d15063e0.png)
例子
创建子进程,并且通过子进程执行pwd命令
const { spawn } = require('child_process');
const child = spawn('pwd');
child.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
child.stderr.on('data', (data) => {
console.log(`stderr: ${data}`);
});
child.on('exit', function (code, signal) {
console.log('child process exited with ' +
`code ${code} and signal ${signal}`);
});
使用exec创建子进程
exec方法接收要运行的命令,创建子进程并执行命令,最后将子进程的输出以回调函数参数的形式返回。
![](https://img.haomeiwen.com/i5476585/bedea026d949eebf.png)
例子
使用exec创建子进程并执行pwd方法
require('child_process').exec('pwd', {encoding: ‘utf-8’}, function(err, stdout, stderr) {
if (err) {
console.log(error.stack);
console.log('Error code: ' + error.code);
console.log('Signal received: ' + error.signal);
}
//console.log(err, stdout, stderr);
console.log('data : ' + stdout);
}).on('exit', function (code) {
console.log('子进程已退出, 退出码 ' + code);
});
使用execFile创建子进程
execFile和exec功能大部分一样,区别是execFile输入一个文件,exec会创建一个shell,execFile会直接运行进程,这使得execFile比spawn和exec都高效。
例子
使用execFile创建子进程,并运行脚本文件
让我们创建两个 js 文件 support.js 和 master.js
support.js 代码
pwd
master.js代码
const child_process = require('child_process');
var workerProcess = child_process.exec('support.js ', function (error, stdout, stderr) {
if (error) {
console.log(error.stack);
console.log('Error code: '+error.code);
console.log('Signal received: '+error.signal);
}
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
});
workerProcess.on('exit', function (code) {
console.log('子进程已退出,退出码 '+code);
});
fork方法
fork方法是spawn方法的特殊形式,主要区别是fork创建的子进程会和父进程之间建立通信管道,子进程可以通过send函数向父进程发送信息,父进程也可以通过send向子进程发送信息。
例子
父进程和子进程发送消息
父进程方法
const { fork } = require('child_process');
const forked = fork('child.js');
forked.on('message', (msg) => {
console.log('Message from child', msg);
});
forked.send({ hello: 'world' });
子进程方法
process.on('message', (msg) => {
console.log('Message from parent:', msg);
});
let counter = 0;
setInterval(() => {
process.send({ counter: counter++ });
}, 1000);
网友评论