新聞中心
1. 回顧進(jìn)程和線程的定義
-
進(jìn)程(Process)是計(jì)算機(jī)中的程序關(guān)于某數(shù)據(jù)集合上的一次運(yùn)行活動(dòng),是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位。

為橋西等地區(qū)用戶提供了全套網(wǎng)頁設(shè)計(jì)制作服務(wù),及橋西網(wǎng)站建設(shè)行業(yè)解決方案。主營業(yè)務(wù)為做網(wǎng)站、成都網(wǎng)站建設(shè)、橋西網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長期合作。這樣,我們也可以走得更遠(yuǎn)!
-
線程(Thread)是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位。它被包含在進(jìn)程之中,是進(jìn)程中的實(shí)際運(yùn)作單位。
2. Node.js的單線程
Node特點(diǎn)主線程是單線程的 一個(gè)進(jìn)程只開一個(gè)主線程,基于事件驅(qū)動(dòng)的、異步非阻塞I/O,可以應(yīng)用于高并發(fā)場(chǎng)景。
Nodejs中沒有多線程,為了充分利用多核cpu,可以使用子進(jìn)程實(shí)現(xiàn)內(nèi)核的負(fù)載均衡,那我們就要解決以下問題:
-
Node.js 做耗時(shí)的計(jì)算時(shí)候阻塞問題。
-
Node.js如何開啟多進(jìn)程。
-
開發(fā)過程中如何實(shí)現(xiàn)進(jìn)程守護(hù)。
3. 場(chǎng)景實(shí)例
- const http = require('http'); http.createServer((req,res)=>{ if(req.url === '/sum'){ // 求和 let sum = 0; for(let i = 0 ; i < 10000000000 ;i++){ sum+=i; } res.end(sum+'') }else{ res.end('end'); } }).listen(3000); // 這里我們先訪問/sum,在新建一個(gè)瀏覽器頁卡訪問/ // 會(huì)發(fā)現(xiàn)要等待/sum路徑處理后才能處理/路徑
4. 開啟進(jìn)程
Node.js 進(jìn)程創(chuàng)建,是通過child_process模塊實(shí)現(xiàn)的:
-
child_process.spawn() 異步生成子進(jìn)程。
-
child_process.fork() 產(chǎn)生一個(gè)新的Node.js進(jìn)程,并使用建立的IPC通信通道調(diào)用指定的模塊,該通道允許在父級(jí)和子級(jí)之間發(fā)送消息。
-
child_process.exec() 產(chǎn)生一個(gè)shell并在該shell中運(yùn)行命令。
-
child_process.execFile() 無需產(chǎn)生shell。
4.1. spawn
spawn 產(chǎn)卵,可以通過此方法創(chuàng)建一個(gè)子進(jìn)程:
- let { spawn } = require("child_process"); let path = require("path"); // 通過node命令執(zhí)行sub_process.js文件 let childProcess = spawn("node",['sub_process.js'], { cwd: path.resolve(__dirname, "test"), // 找文件的目錄是test目錄下 stdio: [0, 1, 2] }); // 監(jiān)控錯(cuò)誤 childProcess.on("error", function(err) { console.log(err); }); // 監(jiān)聽關(guān)閉事件 childProcess.on("close", function() { console.log("close"); }); // 監(jiān)聽退出事件 childProcess.on("exit", function() { console.log("exit"); });
- stido 這個(gè)屬性非常有特色,這里我們給了0,1,2這三個(gè)值分別對(duì)應(yīng)住進(jìn)程的 process.stdin , process.stdout 和 process.stderr 這代表著主進(jìn)程和子進(jìn)程共享標(biāo)準(zhǔn)輸入和輸出:
- let childProcess = spawn("node",['sub_process.js'], { cwd: path.resolve(__dirname, "test"), // 找文件的目錄是test目錄下 stdio: [0, 1, 2] });
可以在當(dāng)前進(jìn)程下打印 sub_process.js 執(zhí)行結(jié)果默認(rèn)在不提供stdio參數(shù)時(shí)為 stdio:['pipe'] ,也就是只能通過流的方式實(shí)現(xiàn)進(jìn)程之間的通信:
- let { spawn } = require("child_process"); let path = require("path"); // 通過node命令執(zhí)行sub_process.js文件 let childProcess = spawn("node",['sub_process.js'], { cwd: path.resolve(__dirname, "test"), stdio:['pipe'] // 通過流的方式 }); // 子進(jìn)程讀取寫入的數(shù)據(jù) childProcess.stdout.on('data',function(data){ console.log(data); }); // 子進(jìn)程像標(biāo)準(zhǔn)輸出中寫入 process.stdout.write('hello');
使用 ipc 方式通信,設(shè)置值為 stdio:['pipe','pipe','pipe','ipc'] 可以通過 on('message') 和 send 方式進(jìn)行通信:
- let { spawn } = require("child_process"); let path = require("path"); // 通過node命令執(zhí)行sub_process.js文件 let childProcess = spawn("node",['sub_process.js'], { cwd: path.resolve(__dirname, "test"), stdio:['pipe','pipe','pipe','ipc'] // 通過流的方式 }); // 監(jiān)聽消息 childProcess.on('message',function(data){ console.log(data); }); // 發(fā)送消息 process.send('hello');
還可以傳入 ignore 進(jìn)行忽略,傳入 inherit 表示默認(rèn)共享父進(jìn)程的標(biāo)準(zhǔn)輸入和輸出。
產(chǎn)生獨(dú)立進(jìn)程:
- let { spawn } = require("child_process"); let path = require("path"); // 通過node命令執(zhí)行sub_process.js文件 let child = spawn('node',['sub_process.js'],{ cwd:path.resolve(__dirname,'test'), stdio: 'ignore', detached:true // 獨(dú)立的線程 }); child.unref(); // 放棄控制
4.2. fork
衍生新的進(jìn)程,默認(rèn)就可以通過 ipc 方式進(jìn)行通信:
- let { fork } = require("child_process"); let path = require("path"); // 通過node命令執(zhí)行sub_process.js文件 let childProcess = fork('sub_process.js', { cwd: path.resolve(__dirname, "test"), }); childProcess.on('message',function(data){ console.log(data); });
fork 是基于 spawn 的,可以多傳入一個(gè) silent 屬性來設(shè)置是否共享輸入和輸出。
fork原理:
- function fork(filename,options){ let stdio = ['inherit','inherit','inherit'] if(options.silent){ // 如果是安靜的 就忽略子進(jìn)程的輸入和輸出 stdio = ['ignore','ignore','ignore'] } stdio.push('ipc'); // 默認(rèn)支持ipc的方式 options.stdio = stdio return spawn('node',[filename],options) }
到了這里我們就可以解決“3.場(chǎng)景實(shí)例”中的場(chǎng)景實(shí)例了:
- const http = require('http'); const {fork} = require('child_process'); const path = require('path'); http.createServer((req,res)=>{ if(req.url === '/sum'){ let childProcess = fork('calc.js',{ cwd:path.resolve(__dirname,'test') }); childProcess.on('message',function(data){ res.end(data+''); }) }else{ res.end('ok'); } }).listen(3000);
4.3. execFile
通過 node 指令,直接執(zhí)行某個(gè)文件:
- let childProcess = execFile("node",['./test/sub_process'],function(err,stdout,stdin){ console.log(stdout); });
內(nèi)部調(diào)用的是 spawn 方法。
4.4. exec
- let childProcess = exec("node './test/sub_process'",function(err,stdout,stdin){ console.log(stdout) });
內(nèi)部調(diào)用的是 execFile ,其實(shí)以上三個(gè)方法都是基于 spawn 的。
5. cluster
Node.js的單個(gè)實(shí)例在單個(gè)線程中運(yùn)行。為了利用多核系統(tǒng),用戶有時(shí)會(huì)希望啟動(dòng)Node.js進(jìn)程集群來處理負(fù)載。 自己通過進(jìn)程來實(shí)現(xiàn)集群。
子進(jìn)程與父進(jìn)程共享HTTP服務(wù)器 fork實(shí)現(xiàn):
- let http = require('http'); let { fork } = require('child_process'); let fs = require('fs'); let net = require('net'); let path = require('path'); let child = fork(path.join(__dirname, '8.child.js')); let server = net.createServer(); server.listen(8080, '127.0.0.1', function () { child.send('server', server); console.log('父進(jìn)程中的服務(wù)器已經(jīng)創(chuàng)建'); let httpServer = http.createServer(); httpServer.on('request', function (req, res) { if (req.url != '/favicon.ico') { let sum = 0; for (let i = 0; i < 100000; i++) { sum += 1; } res.write('客戶端請(qǐng)求在父進(jìn)程中被處理。'); res.end('sum=' + sum); } }); httpServer.listen(server); });
- let http = require('http'); process.on('message', function (msg, server) { if (msg == 'server') { console.log('子進(jìn)程中的服務(wù)器已經(jīng)被創(chuàng)建'); let httpServer = http.createServer(); httpServer.on('request', function (req, res) { if (req.url != '/favicon.ico') { sum = 0; for (let i = 0; i < 10000; i++) { sum += i; } res.write('客戶端請(qǐng)求在子進(jìn)程中被處理'); res.end('sum=' + sum); } }); httpServer.listen(server); } });
進(jìn)程與父進(jìn)程共享socket對(duì)象:
- let { fork } = require('child_process'); let path = require('path'); let child = fork(path.join(__dirname, '11.socket.js')); let server = require('net').createServer(); server.on('connection', function (socket) { if (Date.now() % 2 == 0) { child.send('socket', socket); } else { socket.end('客戶端請(qǐng)求被父進(jìn)程處理!'); } }); server.listen(41234, );
- process.on('message', function (m, socket) { if (m === 'socket') { socket.end('客戶端請(qǐng)求被子進(jìn)程處理.'); } });
使用cluster模塊更加方便:
- let cluster = require("cluster"); let http = require("http"); let cpus = require("os").cpus().
網(wǎng)站名稱:Node.js中的進(jìn)程與線程
轉(zhuǎn)載來于:http://m.5511xx.com/article/cophccp.html


咨詢
建站咨詢
