简介
nodejs的main event loop是单线程的,nodejs本身也维护着Worker Pool用来处理一些耗时的操作,我们还可以通过使用nodejs提供的worker_threads来手动创建新的线程来执行自己的任务。
本文将会介绍一种新的执行nodejs任务的方式,child process。
child process
lib/child_process.js提供了child_process模块,通过child_process我们可以创建子进程。
注意,worker_threads创建的是子线程,而child_process创建的是子进程。
在child_process模块中,可以同步创建进程也可以异步创建进程。同步创建方式只是在异步创建的方法后面加上Sync。
创建出来的进程用ChildProcess类来表示。
我们看下ChildProcess的定义:
interface ChildProcess extends events.EventEmitter { stdin: Writable | null; stdout: Readable | null; stderr: Readable | null; readonly channel"htmlcode">const { spawn } = require('child_process'); const ls = spawn('ls', ['-lh', '/usr']); ls.stdout.on('data', (data) => { console.log(`stdout: ${data}`); }); ls.on('close', (code) => { console.log(`子进程使用代码 $[code] 关闭所有 stdio`); }); ls.on('exit', (code) => { console.log(`子进程使用代码 $[code] 退出`); });最后是message事件,当子进程使用process.send() 发送消息的时候就会被触发。
ChildProcess中有几个标准流属性,分别是stderr,stdout,stdin和stdio。
stderr,stdout,stdin很好理解,分别是标准错误,标准输出和标准输入。
我们看一个stdout的使用:
const { spawn } = require('child_process'); const subprocess = spawn('ls'); subprocess.stdout.on('data', (data) => { console.log(`接收到数据块 ${data}`); });stdio实际上是stderr,stdout,stdin的集合:
readonly stdio: [ Writable | null, // stdin Readable | null, // stdout Readable | null, // stderr Readable | Writable | null | undefined, // extra Readable | Writable | null | undefined // extra ];其中stdio[0]表示的是stdin,stdio[1]表示的是stdout,stdio[2]表示的是stderr。
如果在通过stdio创建子进程的时候,这三个标准流被设置为除pipe之外的其他值,那么stdin,stdout和stderr将为null。
我们看一个使用stdio的例子:
const assert = require('assert'); const fs = require('fs'); const child_process = require('child_process'); const subprocess = child_process.spawn('ls', { stdio: [ 0, // 使用父进程的 stdin 用于子进程。 'pipe', // 把子进程的 stdout 通过管道传到父进程 。 fs.openSync('err.out', 'w') // 把子进程的 stderr 定向到一个文件。 ] }); assert.strictEqual(subprocess.stdio[0], null); assert.strictEqual(subprocess.stdio[0], subprocess.stdin); assert(subprocess.stdout); assert.strictEqual(subprocess.stdio[1], subprocess.stdout); assert.strictEqual(subprocess.stdio[2], null); assert.strictEqual(subprocess.stdio[2], subprocess.stderr);通常情况下父进程中维护了一个对子进程的引用计数,只有在当子进程退出之后父进程才会退出。
这个引用就是ref,如果调用了unref方法,则允许父进程独立于子进程退出。
const { spawn } = require('child_process'); const subprocess = spawn(process.argv[0], ['child_program.js'], { detached: true, stdio: 'ignore' }); subprocess.unref();最后,我们看一下如何通过ChildProcess来发送消息:
subprocess.send(message[, sendHandle[, options]][, callback])其中message就是要发送的消息,callback是发送消息之后的回调。
sendHandle比较特殊,它可以是一个TCP服务器或socket对象,通过将这些handle传递给子进程。子进程将会在message事件中,将该handle传递给Callback函数,从而可以在子进程中进行处理。
我们看一个传递TCP server的例子,首先看主进程:
const subprocess = require('child_process').fork('subprocess.js'); // 打开 server 对象,并发送该句柄。 const server = require('net').createServer(); server.on('connection', (socket) => { socket.end('由父进程处理'); }); server.listen(1337, () => { subprocess.send('server', server); });再看子进程:
process.on('message', (m, server) => { if (m === 'server') { server.on('connection', (socket) => { socket.end('由子进程处理'); }); } });可以看到子进程接收到了server handle,并且在子进程中监听connection事件。
下面我们看一个传递socket对象的例子:
onst { fork } = require('child_process'); const normal = fork('subprocess.js', ['normal']); const special = fork('subprocess.js', ['special']); // 开启 server,并发送 socket 给子进程。 // 使用 `pauseOnConnect` 防止 socket 在被发送到子进程之前被读取。 const server = require('net').createServer({ pauseOnConnect: true }); server.on('connection', (socket) => { // 特殊优先级。 if (socket.remoteAddress === '74.125.127.100') { special.send('socket', socket); return; } // 普通优先级。 normal.send('socket', socket); }); server.listen(1337);subprocess.js的内容:
process.on('message', (m, socket) => { if (m === 'socket') { if (socket) { // 检查客户端 socket 是否存在。 // socket 在被发送与被子进程接收这段时间内可被关闭。 socket.end(`请求使用 ${process.argv[2]} 优先级处理`); } } });主进程创建了两个subprocess,一个处理特殊的优先级, 一个处理普通的优先级。
异步创建进程
child_process模块有4种方式可以异步创建进程,分别是child_process.spawn()、child_process.fork()、child_process.exec() 和 child_process.execFile()。
先看一个各个方法的定义:
child_process.spawn(command[, args][, options]) child_process.fork(modulePath[, args][, options]) child_process.exec(command[, options][, callback]) child_process.execFile(file[, args][, options][, callback])其中child_process.spawn是基础,他会异步的生成一个新的进程,其他的fork,exec和execFile都是基于spawn来生成的。
fork会生成新的Node.js 进程。
exec和execFile是以新的进程执行新的命令,并且带有callback。他们的区别就在于在windows的环境中,如果要执行.bat或者.cmd文件,没有shell终端是执行不了的。这个时候就只能以exec来启动。execFile是无法执行的。
或者也可以使用spawn。
我们看一个在windows中使用spawn和exec的例子:
// 仅在 Windows 上。 const { spawn } = require('child_process'); const bat = spawn('cmd.exe', ['/c', 'my.bat']); bat.stdout.on('data', (data) => { console.log(data.toString()); }); bat.stderr.on('data', (data) => { console.error(data.toString()); }); bat.on('exit', (code) => { console.log(`子进程退出,退出码 $[code]`); });const { exec, spawn } = require('child_process'); exec('my.bat', (err, stdout, stderr) => { if (err) { console.error(err); return; } console.log(stdout); }); // 文件名中包含空格的脚本: const bat = spawn('"my script.cmd"', ['a', 'b'], { shell: true }); // 或: exec('"my script.cmd" a b', (err, stdout, stderr) => { // ... });同步创建进程
同步创建进程可以使用child_process.spawnSync()、child_process.execSync() 和 child_process.execFileSync() ,同步的方法会阻塞 Node.js 事件循环、暂停任何其他代码的执行,直到子进程退出。
通常对于一些脚本任务来说,使用同步创建进程会比较常用。
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 小骆驼-《草原狼2(蓝光CD)》[原抓WAV+CUE]
- 群星《欢迎来到我身边 电影原声专辑》[320K/MP3][105.02MB]
- 群星《欢迎来到我身边 电影原声专辑》[FLAC/分轨][480.9MB]
- 雷婷《梦里蓝天HQⅡ》 2023头版限量编号低速原抓[WAV+CUE][463M]
- 群星《2024好听新歌42》AI调整音效【WAV分轨】
- 王思雨-《思念陪着鸿雁飞》WAV
- 王思雨《喜马拉雅HQ》头版限量编号[WAV+CUE]
- 李健《无时无刻》[WAV+CUE][590M]
- 陈奕迅《酝酿》[WAV分轨][502M]
- 卓依婷《化蝶》2CD[WAV+CUE][1.1G]
- 群星《吉他王(黑胶CD)》[WAV+CUE]
- 齐秦《穿乐(穿越)》[WAV+CUE]
- 发烧珍品《数位CD音响测试-动向效果(九)》【WAV+CUE】
- 邝美云《邝美云精装歌集》[DSF][1.6G]
- 吕方《爱一回伤一回》[WAV+CUE][454M]