[譯] 在 Nodejs 中運行多線程

原文:Going Multithread with Node.jsnode

Node.js 由於其設計而飽受批評。 與 Java, CPython 等編程語言相比,Node.js 不能直接訪問線程顯得有些奇怪。咱們怎麼能併發地執行任務呢?git

好吧,實際上早在 Node.js 11 以前咱們就能夠使用 cluster 模塊來併發/並行地執行代碼,正如 前一篇文章 所述。github

可是若是咱們處在一臺只有一個核的服務器上呢,咱們要怎麼作?數據庫

在 Node.js 11 裏咱們有了 worker_thread 模塊,它容許咱們在單核上建立(spawn)多個線程。實際上在 Node.js 10 裏面咱們也能經過添加 --experimental-worker 標誌來使用這個模塊,可是在 Node.js 11 裏咱們終於不用再加它了!編程

一個簡單的用例

讓咱們假設須要建立一個包含 100 萬個用戶 的文件,每一個用戶由 first name、middle name 和 last name 組成。數組

我發現了下面這個了不得的 Github 項目,它能提供一個包含 first、middle 和 last names 的數組。咱們將在項目中使用這些 JSON 文件:promise

dominictarr/random-name服務器

讓咱們經過下面的文件結構建立一個新項目:多線程

那麼讓咱們從 main.js 文件開始:併發

如你所見,咱們使用了 fs-extra 包。它和 fs 很像,可是全部的函數都返回一個 promise。它解決了這種操做帶來的一個大問題:內存使用。實際上,若是咱們嘗試使用 Node.js 打開太多文件,它將產生一個異常而後殺掉主進程,由於它沒法同時處理全部打開的這些文件(而且內存不足)。在咱們的 for 循環中,await 會在操做結束前使循環停止:經過這種方式,咱們在每一個迭代中就老是隻會打開一個文件。

讓咱們看一下 utils/index.js 文件:

在這裏咱們僅僅只是從任意數組裏面隨機取出一個值。這在咱們須要獲取一個隨機 first、middle 或 last name 時很是有用。

在個人機器(2016 MacBook Pro, 2,7 GHz Intel Core i7, 16GB RAM)上執行代碼,完成任務一共花費了 3 分 32 秒。讓咱們看看如何用 Node.js 工做線程來提升性能!

使用多線程

爲了在這個簡單的程序中採用多線程,咱們須要對咱們的代碼進行一些修改。讓咱們從 main.js 文件開始:

首先,咱們須要從 worker_threads 模塊中引入 Worker 類。它容許咱們在任何須要的時候建立一個新工人(線程)。

接下來咱們能夠設置要建立的線程數量:在這種狀況下,我決定只建立 10 個線程。

而後咱們須要計算每一個線程須要生成名字的數量;這很簡單,咱們只須要用名字的總數除以線程的數量。

對每個線程,咱們須要建立一個新的 Worker。如你所見,這段代碼將被放在 worker.js 文件。

咱們將給新的 Worker 發送一些數據來告訴它須要建立多少名字和存放在哪裏(輸出文件)。

咱們將一直偵聽錯誤和退出,這樣咱們就能知道在咱們的工做線程裏面發生了什麼事情。

如今讓咱們看看 worker.js 文件作了什麼:

基本上它的代碼和原先的 main.js 文件同樣。每當咱們存儲一個新名字時,咱們會將它發送回主線程,因此它能保持對線程內部的追蹤,讓咱們知道里面發生了什麼。

結果如何?咱們只用 1 分 24 秒完成了相同的操做!比單線程版本 快了 37%

其餘用例

當你須要執行一個 CPU 密集型任務時,工做線程 是一個了不得的解決方案。它們使文件系統相關的操做更快,而且在須要執行任何類型的併發操做時幫助很大。最棒的是,如咱們前面提到的,它們在單核機器上也能工做,因此它們能在任何服務上保證更好的性能。

實際上,我已經在一個大規模的上傳操做中使用了 工做線程,在那裏我須要檢查上百萬的用戶而且存儲他們的數據到數據庫。採用多線程的方式,操做速度比對應的單線程快了 10 倍。

我還在圖片操做裏使用到了 工做線程。我須要對單個圖片構建三個縮略圖(不一樣尺寸的),在這種操做裏,多線程的方式一樣幫我節約了時間。

如你所見,工做線程 模塊能夠很好地幫助你提升性能,因此若是它在某種方式下幫助你了,請告訴我吧!

相關文章
相關標籤/搜索