一文搞懂 Node.js 中的多線程和多進程

做者:Nastassia Ovchinnikova

翻譯:瘋狂的技術宅javascript

原文:https://flatlogic.com/blog/mu...前端

未經容許嚴禁轉載java

Node.js 是一個免費的跨平臺 JavaScript 運行時環境,儘管它本質上是單線程的,可是能夠在後臺使用多個線程來執行異步代碼。node

因爲 Node.js 的非阻塞性質,不一樣的線程執行不一樣的回調,這些回調首先委託給事件循環。 Node.js 運行時負責處理全部這一切。程序員

爲何要使用NodeJS?

JavaScript 最初是做爲一種單線程編程語言構建的,僅在 Web 瀏覽器中運行。這意味着在一個過程當中,只有一組指令可以在給定的時間執行。web

僅在當前代碼塊的執行完成後,才移至下一個代碼塊。可是,JavaScript 的單線程性質使實現變得容易。面試

最初,JavaScript 對於僅用於向網站添加少許交互。因此並無對多線程的需求。可是時代已經變了,用戶要求也愈來愈高,JavaScript 已成爲「Web 上流行的編程語言」。編程

多線程如今變得很廣泛。因爲 JavaScript 是單線程語言,所以沒法在其中實現多線程。幸運的是,在這種狀況下,有一個很好的解決方法:Node.js。segmentfault

Node.js 框架並很多,這要歸功於 JavaScript 運行時環境(尤爲是 JavaScript)的廣泛流行。在繼續本文以前,讓咱們瞭解一些有關 Node.js 的重要觀點:後端

  1. 能夠用 send 函數將消息從子進程傳遞到其餘子進程和主進程
  2. 支持 fork 多個進程
  3. 主進程和子進程之間不共享狀態

爲何要 fork 進程?

在兩種狀況下,咱們須要 fork 一個流程:

  1. 經過將任務委派給其餘進程來提升速度
  2. 用於釋放內存和卸載單個進程

能夠將數據發送到子進程,也能夠將其送回。

Node.js 的方式

Node.js 使用兩種類型的線程:

  1. 經過事件循環處理主線程,
  2. 工做池中有許多輔助線程

事件循環負責獲取回調或函數,並將其註冊以供未來執行。它與正確的 JavaScript 代碼在同一線程中運行。一旦 JavaScript 操做阻塞了線程,事件循環也會被阻塞。

工做池是一個執行模型,負責產生和處理不一樣的線程。它同步執行任務,而後將結果返回到事件循環,最後事件循環將結果提供給回調。

總而言之,工做池負責異步 I/O 操做,即與系統磁盤和網絡的交互。像 fs 和 crypto 這樣的模塊是使用工做池的主要模塊。

因爲工做池是在 libuv 庫中實現的,Node.js 在 JS 和 C++ 之間進行內部通訊時會稍有延遲。不過這幾乎是不可察覺的。

一切都很好,直到咱們遇到同步執行復雜操做的要求。任何須要大量時間執行的函數都會致使主線程阻塞。

若是程序具備多個佔用大量 CPU 的函數,將會致使服務器吞吐量的顯着降低。在最壞的狀況下,服務器將會失去響應,而且沒法將任務委派給工做池。

諸如 AI、大數據和機器學習之類的領域沒法從 Node.js 中受益,由於這些操做阻塞了主線程,並使服務器失去響應。可是這隨着 Node.js v10.5.0 的到來而改變,該版本增長了對多線程的支持。

併發和 CPU 綁定任務的挑戰

在 JavaScript 中創建併發可能很困難。容許多個線程訪問相同的內存會致使競爭狀態,這不只使故障難以重現,並且解決起來也很困難。

Node.js 最初被實現爲基於異步 I/O 的服務器端平臺。經過簡單地消除線程需求,這使不少事情變得容易。是的,Node.js 程序是單線程的,但不是典型的方式。

咱們能夠在 Node.js 中並行運行,可是不須要建立線程。操做系統和虛擬機共同並行使用 I/O,而後在須要將數據發送回 JavaScript 代碼時,JS 代碼在單個線程中運行。

除 JS 代碼外,全部內容均在 Node.js 中並行運行。與異步塊不一樣,JS 的同步塊老是一次執行一次。與代碼執行相比,等待 JS 中產生 I/O 事件所話費的時間要多得多。

Node.js 程序僅調用所需的函數或回調,而不會阻止其餘代碼的執行。最初 JavaScript 和 Node.js 都不打算處理 CPU 密集型或 CPU 綁定的任務。

當代碼最少時,執行將會是敏捷的。可是計算量越大,執行速度就越慢。

若是你仍然嘗試在 JS 和 Node 中完成 CPU 密集型任務,那麼將會使瀏覽器中的 UI 凍結並對全部 I/O 事件進行排隊處理。儘管如此,咱們已經走了很遠。如今有了 worker_threads 模塊。

worker_threads 模塊使多線程變得簡單

Node.js v10.5.0 於 2018 年 6 月發佈,引入了 worker_threads 模塊。它有助於在流行的 JavaScript 運行時環境中實現併發。該模塊容許建立功能齊全的多線程 Node.js 應用。

從技術上講,工做線程是在單獨的線程中產生的一些代碼。要開始使用輔助線程,須要先導入 worker_threads 模塊。以後須要建立 Worker 類的實例以建立工做線程。

建立 Worker 類的實例時,有兩個參數:

  1. 第一個參數提供擴展名 .js 或 .mjs 的文件路徑,其中包含工做程序線程的代碼,
  2. 第二個參數提供了一個包含 workerData 屬性的對象,該屬性包含工做線程開始執行時將訪問的數據

輔助線程可以調度多個消息事件。所以,回調方法優先於返回 promise。

工做線程之間的通訊是基於事件的,即偵聽器設置爲在工做線程發送事件後當即調用。最多見的 4 個事件是:

worker.on('error', (error) => {});
  1. 當工做線程中有未捕獲的異常時發出。接下來工做線程終止,而且該錯誤能夠做爲回調中的第一個參數使用。
worker.on('exit', (exitCode) => {})
  1. 當輔助線程退出時發出。若是在工做線程中調用了 process.exit(),則會將 exitCode 提供給回調。若是 worker.terminate() 終止工做線程,則代碼爲 1。
worker.on('message', (data) => {});
  1. 當工做線程將數據發送到父線程時發出。
worker.on('online', () => {});
  1. 當工做線程中止解析 JS 代碼並開始執行時發出。儘管不經常使用,但 online 事件在特定狀況下可能會提供更多信息。

使用工做線程的方式

有兩種使用工做線程的方法:

  • 方法 1 – 涉及產生工做線程,執行其代碼並將結果發送到父線程。此方法須要每次爲新任務從頭建立新的 worker 線程。
  • 方法 2 – 涉及生成 worker 線程併爲消息事件設置偵聽器。每次觸發該消息時,輔助線程都會執行代碼,並將結果發送回父線程。輔助線程保持活動狀態,以備未來使用。

方法 2 也被稱爲工做池。這是由於該方法涉及建立 worker 的工做池,先讓他們等待,並在須要時去調度消息事件來執行任務。

因爲從頭建立工做線程須要建立虛擬機以及解析和執行代碼,所以官方 Node.js 文檔 建議採用方法 2。此外,方法 2 更爲實用,比方法 1 更有效。

worker_threads 模塊中可用的重要屬性

  • isMainThread – 當不在工做線程內操做時,此屬性爲 true。若是須要,則能夠在 worker 文件的開頭包含一個簡單的 if 語句。這樣能夠確保它僅做爲工做線程運行。
  • parentPort – MessagePort 的實例,用於與父線程進行通訊。
  • threadId – 分配給工做線程的惟一標識符。
  • workerData – 包含在 worker 線程的構造函數中的數據。

Node.js 中的多進程

爲了使 Node.js 利用多核系統的功能,能夠用一些進程。流行的 javascript 運行時環境中有稱被爲 cluster 的模塊,該模塊提供對多進程的支持。

使用 cluster 模塊能夠產生多個子進程,這些子進程能夠共享一個公共端口。當子進程投入使用時,使用 NodeJS 的系統能夠處理更大的工做量。

後端的 Node.js

互聯網已經成爲全球數以百萬計公司的首選平臺。所以,爲使一家企業發揮最大潛力,並在此過程當中脫穎而出,必須擁有強大的網絡形象。

這一切都始於一個強大而直觀的網站。要打造一個完美無瑕的網站,重要的是選擇最佳的前端和後端技術。儘管本質上是單線程的,但 Node.js 是開發後端 Web 服務的首選。

儘管有大量的後端多線程選擇,但知名公司仍是喜歡 Node.js。這是由於 Node.js 提供了在 JavaScript 中使用多線程的變通方法,而 JavaScript 已是「Web上最流行的編程語言」。

總結

worker_threads 模塊提供了一種在 Node.js 程序中實現多線程的簡便方法。經過將繁重的計算委派給工做線程,能夠顯着提升服務器的吞吐量。

藉助對多線程的支持,Node.js 將繼續吸引愈來愈多的來自 AI、大數據和機器學習等計算密集型領域的開發人員、工程師和其餘專業人員。


本文首發微信公衆號:前端先鋒

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎繼續閱讀本專欄其它高贊文章:


相關文章
相關標籤/搜索