通過原始碼分析nodejs執行緒架構

2021-10-04 11:17:48 字數 3022 閱讀 3905

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,...