nodejs支援了程序之後,又支援了執行緒。類似瀏覽器端的web worker。因為nodejs是單執行緒的,但是底層又實現了乙個執行緒池,接著實現了程序,又實現了執行緒。一下變得混亂起來,我們要了解這些功能的實現原理,才能更好地使用他。上篇大致分析了程序的原理,這一篇來講一下執行緒的原理。只有了解執行緒的實現,才能知道什麼時候應該用執行緒,為什麼可以用執行緒。
執行緒的實現也非常複雜。雖然底層只是對執行緒庫的封裝,但是把它和nodejs原本的架構結合起來似乎就變得麻煩起來。下面開始分析建立執行緒的過程。分析執行緒實現之前,我們先看一下執行緒通訊的實現,因為執行緒實現中會用到。通俗來說,他的實現類似乙個管道。
1 message代表乙個訊息。
2 messageportdata是對message操作的乙個封裝和對訊息的承載。
3 messageport是代表通訊的端點。
1 messagechannel類似socket通訊,他包括兩個端點。定義乙個messagechannel相當於建立乙個tcp連線,他首先申請兩個端點(messageport),然後把他們關聯起來。
分析完執行緒通訊的實現,我們開始分析執行緒的實現。nodejs中node_worker.cc實現了執行緒模組的功能。我們看一下這個模組的定義。
node_module_context_aware_internal(worker, node::worker::initworker)
這意味著我們在js裡執行下面的**時
const exportsomething = internalbinding('worker');
拿到的物件是由node::worker::initworker匯出的結果。所以我們看看他匯出了什麼(只列出核心**)。
void initworker(...)
// 匯出額外的一些變數
env->setmethod(target, "getenvmessageport", getenvmessageport);
target
->set(env->context(),
fixed_one_byte_string(env->isolate(), "ismainthread"),
boolean::new(env->isolate(), env->is_main_thread()))
.check();
}
翻譯成js大概是
function new() {}
new.prototype =
module.exports =
了解了c++匯出的變數,我們看看js層的封裝。
class worker extends eventemitter ) = new messagechannel();
this[kpublicport] = port1;
this[kport].postmessage(, [port2]);
this[khandle].startthread();
}}
下面我們逐步分析上面的**。
根據上面的分析我們知道worker函式對應的是c++層的new函式。所以我們看看new函式做了什麼 。
void worker::new(const functioncallbackinfo& args)
是對worker類的封裝。
所以new worker就是定義了乙個物件,並初始化了三個屬性。
我們看到new worker的時候定義了messageport這個屬性。他對應c++的parent_port_ 屬性。是初始化時,父執行緒和子執行緒通訊的一端。另一端在子執行緒中維護。
申請兩個可以互相通訊的端點。使用者後面的執行緒間通訊。
4 儲存主線程端的端點,後續用於通訊。並且給子執行緒傳送一些資訊。告訴子執行緒通訊的埠(子執行緒端的)和執行的js檔名。
this[kpublicport] = port1;
this[kport].postmessage(, [port2]);
在通訊中,至少要存在兩個端,假設x和y。那麼x.postmessage(…),就是給y發資訊。但是根據圖二,我們發現只有乙個messageport。所以上面**中的postmessage只是把訊息快取到訊息佇列裡(messageportdata中)。
開始啟動執行緒。根據c++層匯出的變數我們知道startthread對應函式是startthread。
void worker::startthread(const functioncallbackinfo& args) , static_cast(w)), 0
}
建立乙個執行緒,然後在裡面執行run函式。run函式非常複雜,下面列出幾個步驟。
1 建立乙個通訊端點。和圖二的完成關聯。這樣子執行緒就可以處理剛才快取的訊息了。
2 處理剛才快取的訊息。把parentport設定成父執行緒傳過來的端點。子執行緒就可以和父執行緒通訊了。
const = require('worker_threads');
if (ismainthread) );
worker.postmessage('hello, world!');
} else );
}
3 執行主線程的js檔案(即new worker時傳進來的引數,有很多種形式)。
4 開啟新的事件迴圈,即子執行緒和主線程是分開的兩個事件迴圈。
以上就是nodejs中線程的大致原理。
nodejs流基類原始碼分析
流是對資料生產,消費的一種抽象,今天先分析一下流基類的實現 const ee require events const util require util 流的基類 function stream 繼承事件訂閱分發的能力 util.inherits stream,ee 流的基類只提供了乙個函式就是p...
Hbase WAL執行緒模型原始碼分析
hbase的wal機制是保證hbase使用lsm樹儲存模型把隨機寫轉化成順序寫,並從記憶體read資料,從而提高大規模讀寫效率的關鍵一環。wal的多生產者單消費者的執行緒模型讓wal的寫入變得安全而高效。在文章 wal在regionserver呼叫過程 中從 層面闡述了乙個client的 寫 操作是...
android 執行緒池原始碼分析
一直覺得這塊比較複雜,原因在於需要對資料結構和多執行緒開發比較熟悉。現在從threadpoolexecutor 出發。先看這個建構函式。public threadpoolexecutor int corepoolsize,int maximumpoolsize,long keepalivetime,...