理解Node.js的事件輪詢

前言

總括

智者閱讀羣書,亦閱歷人生

正文

Node.js的兩個基本概念

Node.js的第一個基本概念就是I/O操作開銷是巨大的:

所以,當前變成技術中最大的浪費來自於等待I/O操作的完成。有幾種方法可以解決性能的影響:

  • 同步方式:按次序一個一個的處理請求。:簡單;:任何一個請求都可以阻塞其他所有請求。
  • 開啓新進程:每個請求都開啓一個新進程。:簡單;:大量的鏈接意味着大量的進程。
  • 開啓新線程:每個請求都開啓一個新線程。:簡單,而且跟進程比,對系統內核更加友好,因爲線程比進程輕的多;:不是所有的機器都支持線程,而且對於要處理共享資源的情況,多線程編程會很快變得太過於複雜。

第二個基本概念是每個連接都創建一個新線程是很消耗內存的(例如:你可以對比Nginx回想一下Apache內存耗盡的情景)。

Apache是多線程的:它爲每個請求開啓一個新的線程(或者是進程,這取決於你的配置),當併發連接增多時,你可以看看它是怎麼一點一點耗盡內存的。Nginx和Node.js不是多線程的,因爲線程的消耗太“重”了。它們兩個是單線程、基於事件的,這就把處理衆多連接所產生的線程/進程消耗給消除了。

單線程

確實只有一個線程:你不能並行執行任何代碼,比如:下面的“sleep”將會阻塞sever1秒鐘:

function sleep() {
   var now = new Data().getTime();
   while (new Date().getTime() < now + 1000) {
         // do nothing
   }
}
sleep();

但就我目前學習階段而言,我覺得好多人對於所謂的node單線程是有誤解的。實際上官方給出的“單線程”是具有誤導性的。所謂的單線程是指你的代碼只運行在一個線程上(好多地方都叫它主線程,實際上Javascript的瀏覽器運行環境不也是這麼處理我們寫的Javascript代碼的嘛),而諸多任務的並行處理,就需要多線程了,如下圖:

如上圖,Node.js中的單線程之說指的就是這個主線程,這個主線程有一個循環結構,保持着整個程序(你寫的代碼)的運轉。

事件輪詢

其實上面我們所說的維持主線程運行的循環這部分就是”事件輪詢”,它存在於主線程中,負責不停地調用開發者編寫的代碼。但對開發者是不可見的。so…開發者編寫的代碼是怎樣被調用的呢?看下圖:

如上圖,異步函數在執行結束後,會在事件隊列中添加一個事件(遵循先進先出原則),主線程中的代碼執行完畢後(即一次循環結束),下一次循環開始就在事件隊列中”讀取”事件,然後調用它所對應的回調函數(所以回調函數的執行順序是不一定的)。如果開發者在回調函數中調用了阻塞方法(比如上文中的sleep函數),那麼整個事件輪詢就會阻塞,事件隊列中的事件得不到及時處理。正因爲這樣,nodejs中的一些庫方法均是異步的,也提倡用戶調用異步方法。

var fs = require('fs');
fs.readFile('hello.txt', function (err, data) {  //異步讀取文件
  console.log("read file end");
});
while(1)
{
    console.log("call readFile over");
}

如上代碼,我們雖然使用了異步方法readfile讀取文件,但read file end永遠不會輸出,因爲代碼始終在while循環中,下一次事件輪詢始終沒法開始,也就沒法’讀取’事件隊列調用相應的回調函數了。

最後有一個Node-sample是博主平時積累的一些代碼,包含註釋,彙總成了一個小應用,還是可以看到學習的蛛絲馬跡的。感興趣的您可以看看。

後記

參考文章:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章