child_process中spawn和exec方法的使用

2019-01-29 14:33:07

child_process是nw.exe的一个内置模块,通过它可以实现创建多线程,并可实现主线程和子线程之间的通信。child_process模块中主要使用有两个方法spawn和exec,这两个方法都可以用来创建子线程。除了spawn和exec外,child_process模块还有execFile,fork,spawnSync,execFileSync,execSync,它们都是基于spawn的不同封装。


使用child_process模块模块不外乎几种情况:1.调用系统命令行 2.打开第三方软件 3.打开第三方软件并实现通信。


(一)child_process模块 Demo


var cp = require('child_process'); //子进程

var result = "";

 

cp.exec("ipconfig", function(error, stdout, stderr) {

console.log("error", error);

console.log("stdout", stdout);

console.log("stderr", stderr);

});

 

result = cp.spawn('cmd.exe', ['/s', '/c', 'ipconfig']);

result.on('close', function(code) {

console.log('child process exited with code :' + code);

});

result.stdout.on('data', function(data) {

console.log('stdout: ' + data);

});

result.stderr.on('data', function(data) {

console.log('stderr: ' + data);

});


以上两个方法无论是spawn还是exec都通过调用了命令行ipconfig获取系统相关IP信息,两者得到的结果是一样的,本Demo可看出只不过两种调用的写法不一样而已。



(二)spawn和exec的区别

(1)两者实现的写法不同,参照上述Demo即可了解。


(2)spawn在执行时会返回一个stdout和stderr流对象,为边执行边返回。exec是在执行完成后返回一个完整的buffer,这个buffer的大小应该是200k。如果子进程返回的数据大小超过了200k,程序将会崩溃,同时显示错误信息"Error:maxBuffer exceeded"。


(3)spawn在执行完成后会抛出close事件监听,并返回状态码,通过状态码可以知道子进程是否顺利执行。exec只能通过返回的buffer去识别完成状态,识别起来较为麻烦。


上述(一)中已经介绍如何调用系统命令行,下面为打开第三方软件的例子,如使用exec打开QQ程序:



var cp = require('child_process'); //子进程

var path = "";//第三方根目录

cp.exec(path + "\\QQ.exe", function(error, stdout, stderr) {

console.log("error", error);

console.log("stdout", stdout);

console.log("stderr", stderr);

});


打开第三方软件并通信,如使用spawn,调用ffmpeg进行转码:


var cp = require('child_process'); //子进程

var path = ""; //第三方根目录

result = cp.spawn(path + "\\ffmpeg.exe", ['-y', '-i', "1.mpg", '1.mp4']);

result.on('close', function(code) {

console.log('child process exited with code :' + code);

});

result.stdout.on('data', function(data) {

console.log('stdout: ' + data);

});

result.stderr.on('data', function(data) {

console.log('stderr: ' + data);

});


综上所述,能使用spawn实现子进程的尽量使用spawn,其一可以避免不必要的异常错误,其次在调用上较为规范。如果需要通过child_process模块反复调用某个第三方软件或程序,注意一点就是避免重复打开(如子进程进入死循环,或者子进程没结束就又重复打开),过多的子进程会消耗系统的资源,严重时可能会造成卡顿,死机等情况。如果遇到这种情况,可以通过.kill();方法停止线程运行。如上述使用spawn的例子中均有一个result,停止线程方法为:


result.kill();


如需强制关闭,如强制关闭ffmpeg.exe进程,可用:


function killFFmpeg() {

cp.exec('tasklist | find "ffmpeg.exe"', function(error, stdout, stderr) {

if(stdout != "") {

cp.exec("taskkill /f /t /im ffmpeg.exe", function(error, stdout, stderr) {});

}

});

};


  • 2020-02-02 15:40:36

    Apache Commons IO之IOUtils优雅操作流

    在开发过程中,你肯定遇到过从流中解析数据,或者把数据写入流中,或者输入流转换为输出流,而且最后还要进行流的关闭,原始jdk自带的方法写起来太复杂,还要注意各种异常,如果你为此感到烦恼,那IOUtils可以让我们优雅的操作流。

  • 2020-02-02 19:24:38

    百度视频SDK,突然不能播放

    开发过程中,不知道什么时候开始视频不能播发了,怎么办都不行,其他项目没问题,线上都也没有问题,这可急躁完蛋我了,整了仨小时,还是那熊样。 哎。

  • 2020-02-04 18:43:10

    AssetManager.finalize() Timed Out 解决办法以及分析

    在我们的项目崩溃中,有一个比较常见的bug,就是 java.util.concurrent.TimeoutException android.content.res.AssetManager.finalize() timed out after 10 seconds 意思简单明了,就是说在AssetManager析构的时候发生了超时异常。

  • 2020-02-06 13:32:10

    android.os.NetworkOnMainThreadException

    在Android 4.0以上,网络连接不能放在主线程上,不然就会报错android.os.NetworkOnMainThreadException。但是4.0下版本可以不会报错。

  • 2020-02-07 23:46:44

    You must call removeView() on the child's parent first解决办法

    出现这样的情况最多是发生在recyclerView中,holder复用的过程中,多次添加view,第一次添加的时候view有了父类了,可能就是复用引起的。 我是发生在给recyclerView添加广告view的时候发生的。