首頁  >  文章  >  web前端  >  nodejs 如何處理密集型運算

nodejs 如何處理密集型運算

藏色散人
藏色散人原創
2020-04-18 09:51:342891瀏覽

nodejs 如何處理密集型運算

nodejs 如何處理密集型運算?

Nodejs密集型CPU解決方案

先說一下nodejs單執行緒的優勢:

推薦:《javascript高級教程

高效能,與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 如何處理密集型運算的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn