【程式語言 - NodeJS】 worker-threads

更新於 發佈於 閱讀時間約 9 分鐘
raw-image

早期的 nodejs 為了具有多線程的能力而引入了 cluster 模組, 但這種創建線程的方式會犧牲共享內存, 且數據通信必須透過 json 來傳輸, 因此具有一定的侷限性及性能問題。

而後發展了worker-threads這個模組之後, 具備共享內存的功能, 使其更輕量。

nodejs 在v10.5.0時引入了新的模組worker_threads, 但當時還處於實驗階段, 因此執行程式時需加入參數--experimental-worker才能正確引入該模組, 不過以下實驗採用v12.7.0, 已經不需要額外參數--experimental-worker來開啟模組開關了。

架構

以下的架構圖中, 將從 main.js 中分派可並行的工作給 worker.js 去進行執行, 而 worker.js 在 nodejs 背後的機制相當於 multi threads。

         | ->  worker.js
main.js -| -> worker.js
| -> worker.js

main 與 worker 之間的資料傳遞

  1. 傳遞數據的方式: worker.postMessage(value)。
  2. 接收對方傳遞數據的方式: worker.on('message', callbackFn)。

main.js:

const worker = new Worker('./worker.js');

// 傳遞資料給worker
worker.postMessage('send to worker');
// 監聽: 接收worker回報的資料
worker.on('message', msg => {
console.info(msg);
});

worker.js

import { parentPort } from 'worker_threads';
parentPort.on('message', value => {
console.info(value);
parentPort.postMessage('report to master');
});

實驗

以下範例將實驗相同的運算之下使用單線程與多線程所耗費的時間比較。

題目: 我們會設計有 N 個 worker, 每個 worker 執行 X 次的累加, 每次的累加數字為 Y。

  1. 參數配置:
// 假設8個worker
const workerNum: number = 8;
// 每個worker都做10億次的累加
const perWorkerAccSize: number = 1000000000;
// 每次的累加數字8
const perAccNum: number = 8;

2. 首先我們設計一個累加的函數, 可帶入數字及累加的次數。

const accumulate = (num: number, size: number): number => {
let result: number = 0;
for (let i = 0; i < size; i++) {
result += num;
}
return result;
};

3. 接著我們撰寫單線程的程式:

(() => {
console.info('start accmulate with single...');
const size = workerNum * perWorkerAccSize;
console.info(`do ${size} number to accumulate`);
const start = new Date().getTime();
const sum = accumulate(perAccNum, size);
const end = new Date().getTime();
console.info(`sum: ${sum}, time: ${end - start}`);
})();

4. 設計分派的程式:

/**
* 分派工作
* @param workerNum 工人數量
* @param accNum 每次加總的數字
* @param size 加總的次數
*/
const accWithWorker = async (workerNum: number, accNum: number, size: number): Promise<number> =>
new Promise((resolve, reject) => {
// 紀錄工作做完的次數
let doingCount = 0;
// 總共做完的工作數量
const doneCount = workerNum;
// 加總的最終數量
let sum = 0;
while (--workerNum >= 0) {
console.info(`worker ${workerNum}, do ${size} number to accumulate`);
const worker = new Worker('./dist/worker.js');
// 傳遞加總的數字及次數給worker
worker.postMessage({
accNum,
size
});
// 監聽: worker回報的加總結果
worker.on('message', num => {
sum += num;
if (++doingCount === doneCount) {
resolve(sum);
}
});
}
});

5. 接著最後我們設計每個worker進行加總的工作。

import { parentPort } from 'worker_threads';
parentPort.on('message', (value) => {
const { accNum, size} = value;
parentPort.postMessage(accumulate(accNum, size));
});

const accumulate = (num: number, size: number): number => {
let result: number = 0;
for (let i = 0; i < size; i++) {
result += num;
}
return result;
};

運行結果

可以發現到我們開8個worker進行處理, 時間節省了1/3。

start accmulate with single...
do 8000000000 number to accumulate
sum: 64000000000, time: 9224
start accmulate with 8 worker...
worker 7, do 1000000000 number to accumulate
worker 6, do 1000000000 number to accumulate
worker 5, do 1000000000 number to accumulate
worker 4, do 1000000000 number to accumulate
worker 3, do 1000000000 number to accumulate
worker 2, do 1000000000 number to accumulate
worker 1, do 1000000000 number to accumulate
worker 0, do 1000000000 number to accumulate
sum: 64000000000, time: 2464
留言
avatar-img
留言分享你的想法!
avatar-img
阿Han的沙龍
131會員
292內容數
哈囉,我是阿Han,是一位 👩‍💻 軟體研發工程師,喜歡閱讀、學習、撰寫文章及教學,擅長以圖代文,化繁為簡,除了幫助自己釐清思路之外,也希望藉由圖解的方式幫助大家共同學習,甚至手把手帶您設計出高品質的軟體產品。
你可能也想看
Thumbnail
家中修繕或裝潢想要找各種小零件時,直接上網採買可以省去不少煩惱~看看Sylvia這回為了工地買了些什麼吧~
Thumbnail
家中修繕或裝潢想要找各種小零件時,直接上網採買可以省去不少煩惱~看看Sylvia這回為了工地買了些什麼吧~
Thumbnail
👜簡單生活,從整理包包開始!我的三款愛用包+隨身小物清單開箱,一起來看看我每天都帶些什麼吧🌿✨
Thumbnail
👜簡單生活,從整理包包開始!我的三款愛用包+隨身小物清單開箱,一起來看看我每天都帶些什麼吧🌿✨
Thumbnail
創作者營運專員/經理(Operations Specialist/Manager)將負責對平台成長及收入至關重要的 Partnership 夥伴創作者開發及營運。你將發揮對知識與內容變現、影響力變現的精準判斷力,找到你心中的潛力新星或有聲量的中大型創作者加入 vocus。
Thumbnail
創作者營運專員/經理(Operations Specialist/Manager)將負責對平台成長及收入至關重要的 Partnership 夥伴創作者開發及營運。你將發揮對知識與內容變現、影響力變現的精準判斷力,找到你心中的潛力新星或有聲量的中大型創作者加入 vocus。
Thumbnail
撰寫Python的朋友都知道multithread/multiprocess能為我們帶來效能的改進,減少硬體資源的閒置,但在撰寫的過程中常常會發現到我們所設計的工作池模式會需要將「待辦清單」的工作項目當成參數傳遞進去執行, 除了「待辦清單」之外, 其餘的參數基本上都是固定的, 基於這樣的需求之下
Thumbnail
撰寫Python的朋友都知道multithread/multiprocess能為我們帶來效能的改進,減少硬體資源的閒置,但在撰寫的過程中常常會發現到我們所設計的工作池模式會需要將「待辦清單」的工作項目當成參數傳遞進去執行, 除了「待辦清單」之外, 其餘的參數基本上都是固定的, 基於這樣的需求之下
Thumbnail
這篇想來寫,剛碰到js得時候,為了讓程式可以運作而安裝Node.js 。Node.js 是能夠在伺服器上面運行 JavaScript 的應用平台環境,透過 Node.js 提供的函式庫與執行環境能完成伺服器端服務。此篇幅就直接從純後端的角度切入摟(對不起拉我寫來寫去還是不知道怎麼順順的寫好文章開頭Q
Thumbnail
這篇想來寫,剛碰到js得時候,為了讓程式可以運作而安裝Node.js 。Node.js 是能夠在伺服器上面運行 JavaScript 的應用平台環境,透過 Node.js 提供的函式庫與執行環境能完成伺服器端服務。此篇幅就直接從純後端的角度切入摟(對不起拉我寫來寫去還是不知道怎麼順順的寫好文章開頭Q
Thumbnail
不論我們使用Nodejs的Mongo Client還是Go語言甚至其他語言的套件, 應該都會看到連線選項中有poolSize這類的配置, 以下會介紹什麼是連線池、優點、應注意的事項。 首先來談談連接池之前的單一連接機制, 在配置正確的狀況下可能不會遇到任何問題, 但假設我們的應用同時間有100個用戶
Thumbnail
不論我們使用Nodejs的Mongo Client還是Go語言甚至其他語言的套件, 應該都會看到連線選項中有poolSize這類的配置, 以下會介紹什麼是連線池、優點、應注意的事項。 首先來談談連接池之前的單一連接機制, 在配置正確的狀況下可能不會遇到任何問題, 但假設我們的應用同時間有100個用戶
Thumbnail
由於Javascript本身設計就適合於單線程的應用, 但一般後端應用程式都會支援多個服務來處理client的請求, nodejs中也提供了cluster模組來達成此功能。 Cluster的原理很簡單,由於每個Process都只能用單核心的CPU來運行,那麼就多開幾個來幫忙處理吧! 而這個Clust
Thumbnail
由於Javascript本身設計就適合於單線程的應用, 但一般後端應用程式都會支援多個服務來處理client的請求, nodejs中也提供了cluster模組來達成此功能。 Cluster的原理很簡單,由於每個Process都只能用單核心的CPU來運行,那麼就多開幾個來幫忙處理吧! 而這個Clust
Thumbnail
Web Workers主要提供簡單的API讓網頁在背景執行緒中執行程式而不干擾使用者的操作。 javascript主要功能是與user操作頁面互動及操作dom,試想若使用多執行緒的概念,那麼一個動作是新增至某個dom節點,另一個動作則是修改該dom節點,此時瀏覽器應該使用哪個動作為準? 所以為了避免
Thumbnail
Web Workers主要提供簡單的API讓網頁在背景執行緒中執行程式而不干擾使用者的操作。 javascript主要功能是與user操作頁面互動及操作dom,試想若使用多執行緒的概念,那麼一個動作是新增至某個dom節點,另一個動作則是修改該dom節點,此時瀏覽器應該使用哪個動作為準? 所以為了避免
Thumbnail
Service worker與Web workers相同,也都是一段運行在瀏覽器後台的腳本,提供一些不需要與頁面直接交互的功能(操作dom),主要處理網路相關的問題,可以攔截網路請求進行相對應的優化動作,我們把它想像成與伺服器之間的代理服務器可能會比較容易理解,當網路環境不佳時便回應快取資源,待網路
Thumbnail
Service worker與Web workers相同,也都是一段運行在瀏覽器後台的腳本,提供一些不需要與頁面直接交互的功能(操作dom),主要處理網路相關的問題,可以攔截網路請求進行相對應的優化動作,我們把它想像成與伺服器之間的代理服務器可能會比較容易理解,當網路環境不佳時便回應快取資源,待網路
Thumbnail
執行以上程式碼,然後看到了這個結果: 為什麼「延遲0秒」的函式寫在上方,但在console印出的結果,它還是被排在第二順位? 利用AC教材提供的youtube演講連結一窺究竟: 演講提供了更多JS的細節概念,身為JS新手的我還在消化,但若針對教案提出的問題來回答,加上利用google大神查到MDN的
Thumbnail
執行以上程式碼,然後看到了這個結果: 為什麼「延遲0秒」的函式寫在上方,但在console印出的結果,它還是被排在第二順位? 利用AC教材提供的youtube演講連結一窺究竟: 演講提供了更多JS的細節概念,身為JS新手的我還在消化,但若針對教案提出的問題來回答,加上利用google大神查到MDN的
Thumbnail
介紹 📷 定義 處理序(Process) (大陸:進程): 一個程序運行時,占用全部計算資源的總和 執行緒(Thread) (大陸:線程):是作業系統能夠進行運算排程的最小單位。 大部分情況下,它被包含在行程之中,是行程中的實際運作單位。 C#多線程和異步(一)——基本概念和使用方法 執行緒帶來的
Thumbnail
介紹 📷 定義 處理序(Process) (大陸:進程): 一個程序運行時,占用全部計算資源的總和 執行緒(Thread) (大陸:線程):是作業系統能夠進行運算排程的最小單位。 大部分情況下,它被包含在行程之中,是行程中的實際運作單位。 C#多線程和異步(一)——基本概念和使用方法 執行緒帶來的
Thumbnail
前言 這是第一次寫技術文章,但其實應該也只能說是蒐集很多資料並學習如何透過自己的話解釋的內容,並不能像其他大神可能分享一些很酷的技術,目標就單純是為了完成最後一週的作業(如下)。 走入非同步之前 執行環境(Execution Context) 執行環境堆疊 (Execution stack)
Thumbnail
前言 這是第一次寫技術文章,但其實應該也只能說是蒐集很多資料並學習如何透過自己的話解釋的內容,並不能像其他大神可能分享一些很酷的技術,目標就單純是為了完成最後一週的作業(如下)。 走入非同步之前 執行環境(Execution Context) 執行環境堆疊 (Execution stack)
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News