標籤:浮點運算 app 自動 利用 停止 使用 子進程 iso 參數
首先說一下nodejs單線程的優勢:
高效能,與php相比,避免了頻繁建立切換線程的開銷,執行更加迅速,資源佔用小。
安全執行緒,不用擔心同一變數被多線程讀寫,造成程式崩潰。
單線程的非同步和非阻塞,其實 nodejs底層訪問I/O還是多線程的,阻塞/非阻塞與非同步/同步是兩個不同的概念,同步不代表阻塞,但是阻塞肯定就是同步;有點兒繞口,請聽我舉例,我去食堂打飯,我選擇了A套餐,然後工作人員幫我去配餐,如果我就站在旁邊,等待工作人員給我配餐,這種情況就稱之為同步;若工作人員幫我配餐的同時,排在我後面的人就開始點餐,這樣整個食堂的點餐服務並沒有因為我在等待A套餐而停止,這種情況就稱之為非阻塞。這個例子就簡單說明了同步但非阻塞的情況。再如果我在等待配餐的時候去買飲料,等聽到叫號再回去拿套餐,此時我的飲料也已經買好,這樣我在等待配餐的同時還執行了買飲料的任務,叫號就等於執行了回調,就是非同步非阻塞了。如果我在買飲料的時候,已經叫我的號讓我去拿套餐,可是我等了好久才拿到飲料,所以我可能在大廳叫我的餐號之後很久才拿到A套餐,這也就是單線程的阻塞情況。
多線程:
線程是cpu調度的一個基本單位,一個cpu只能執行一個線程任務。
nodejs也可以執行多行程任務,例如引用TAGG/TAGG2模組,大家可以看一下我上一篇文章,裡面有具體的使用方法。但是不論是tagg/tagg2都是利用pthread庫和V8::Isolate類來實現js多線程功能的,根據規則我們線上程裡執行的函數無法使用nodejs的核心api,例如fs,crypto模組,所以還是有很大的局限性。
多進程:
在支援html5的瀏覽器裡,我們可以使用webworker來將一些耗時的計算丟入worker進程中執行,這樣主進程就不會阻塞,使用者也不會有卡頓的感覺。
這裡我們需要利用nodejs的child_process模組,child_process提供了fork方法,可以啟動一個nodejs檔案,將它視作worker進程,worker 工作完畢後,會把結果send給主進程,然後worker自動結束,這樣我們就利用了多進程解決了主線程阻塞的問題。
var express = require(‘express‘);var fork = require(‘child_process‘).fork;var app = express();app.get(‘/‘, function(req, res){ var worker = fork(‘./work.js‘) //建立一個背景工作處理序 worker.on(‘message‘, function(m) {//接收背景工作處理序計算結果 if(‘object‘ === typeof m && m.type === ‘fibo‘){ worker.kill();//發送殺死進程的訊號 res.send(m.result.toString());//將結果返回用戶端 } }); worker.send({type:‘fibo‘,num:~~req.query.n || 1}); //發送給背景工作處理序計算fibo的數量});app.listen(7878);
我們通過express監聽7878連接埠,對每個使用者的請求都會去fork一個子進程,通過調用worker.send方法將參數n傳遞給子進程,同時監聽子進程發送訊息的message事件,將結果響應給用戶端。
下面是被fork的work.js檔案內容:
var fibo = function fibo (n) {//定義演算法 return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1;}process.on(‘message‘, function(m) {//接收主進程發送過來的訊息 if(typeof m === ‘object‘ && m.type === ‘fibo‘){ var num = fibo(~~m.num); //計算jibo process.send({type: ‘fibo‘,result:num}) //計算完畢返回結果 }});process.on(‘SIGHUP‘, function() { process.exit();//收到kill資訊,進程退出});
我們先定義函數fibo用來計算斐波那契數組,然後監聽了主線程發來的訊息,計算完畢之後將結果send到主線程。同時還監聽process的SIGHUP事件,觸發此事件就進程退出。
這裡我們有一點需要注意,主線程的kill方法並不是真的使子進程退出,而是會觸發子進程的SIGHUP事件,真正的退出還是依靠process.exit()。
總結:
使用child_process模組的fork方法確實可以讓我們很好的解決單線程對cpu密集型任務的阻塞問題,同時又沒有tagg包那樣無法使用Node.js核心api的限制。
單線程非同步Node.js不代表不會阻塞,在主線程做過多的任務可能會導致主線程的卡死,影響整個程式的效能,所以我們要非常小心的處理大量的迴圈,字串拼接和浮點運算等cpu密集型任務,合理的利用各種技術把任務丟給子線程或子進程去完成,保持Node.js主線程的暢通。
線程/進程的使用並不是沒有開銷的,儘可能減少建立和銷毀線程/進程的次數,可以提升我們系統整體的效能和出錯的機率。
Nodejs密集型CPU解決方案