首頁  >  文章  >  web前端  >  聊聊Node.js + worker_threads如何實作多執行緒? (詳解)

聊聊Node.js + worker_threads如何實作多執行緒? (詳解)

青灯夜游
青灯夜游轉載
2022-02-11 20:07:543920瀏覽

本篇文章帶大家了解一下worker_threads 模組,介紹一下在Node中如何使用worker_threads實現多線程,以及利用worker_threads執行斐波那契數列作為實踐例子,希望對大家有所幫助!

聊聊Node.js + worker_threads如何實作多執行緒? (詳解)

通常情況下,Node.js被認為是單執行緒。由主執行緒去依照編碼順序一步一步執行程式碼,一旦遇到同步程式碼阻塞,主執行緒就會被佔用,後續的程式碼的執行都會被卡住。沒錯Node.js的單線程指的是主線程是"單線程"。

為了解決單一執行緒帶來的問題,本文的主角worker_threads出現了。 worker_threads首次在Node.js v10.5.0作為實驗性功能出現,需要命令列帶上--experimental-worker才能使用。直到v12.11.0穩定版才能正式使用。

本文將會介紹worker_threads的使用方式,以及利用worker_threads執行斐波那契數列作為實作範例。

先決條件

閱讀並食用本文,需要先具備:

    ##安裝了
  • Node.js v12.11.0 以上版本
  • 掌握JavaScript 同步與非同步程式設計的基礎知識
  • 掌握Node.js 的工作原理

worker_threads 介紹

worker_threads 模組允許使用並行執行JavaScript 的執行緒。

工作執行緒對於執行 CPU 密集型的 JavaScript 操作很有用。它們對 I/O 密集型的工作幫助不大。 Node.js 內建的非同步 I/O 操作比工作執行緒更有效率。

與 

child_process 或 cluster 不同,worker_threads 可以共享記憶體。它們透過傳輸 ArrayBuffer 實例或共享 SharedArrayBuffer 實例來實現。

由於以下特性,

worker_threads已被證明是充分利用CPU效能的最佳解決方案:

    ##它們運行具有多個執行緒的單一進程。
  • 每個執行緒執行一個事件循環。
  • 每個執行緒執行單一 JS 引擎實例。
  • 每個執行緒執行單一
  • Nodejs

    實例。

worker_threads 如何運作

worker_threads

透過執行主執行緒指定的腳本檔案來工作。每個執行緒都在與其他執行緒隔離的情況下執行。但是,這些線程可以透過訊息通道來回傳遞訊息。

主執行緒

使用worker.postMessage()函數使用訊息通道,而工作執行緒使用parentPort.postMessage()函數。

透過官方範例程式碼加強了解:

const {
  Worker, isMainThread, parentPort, workerData
} = require('worker_threads');

if (isMainThread) {
  module.exports = function parseJSAsync(script) {
    return new Promise((resolve, reject) => {
      const worker = new Worker(__filename, {
        workerData: script
      });
      worker.on('message', resolve);
      worker.on('error', reject);
      worker.on('exit', (code) => {
        if (code !== 0)
          reject(new Error(`Worker stopped with exit code ${code}`));
      });
    });
  };
} else {
  const { parse } = require('some-js-parsing-library');
  const script = workerData;
  parentPort.postMessage(parse(script));
}
上述程式碼

主執行緒

工作執行緒都使用同一份檔案作為執行腳本(__filename為目前執行檔路徑),透過isMainThread來區分主執行緒工作執行緒運行時邏輯。當模組對外暴露方法parseJSAsync被呼叫時候,都會衍生子工作執行緒去執行呼叫parse函數。

worker_threads 具體使用

在本節使用具體範例介紹

worker_threads

的使用##「建立工作執行緒

腳本檔案

workerExample.js:

const { workerData, parentPort } = require('worker_threads')
parentPort.postMessage({ welcome: workerData })
建立主執行緒

腳本檔案

main.js:

const { Worker } = require('worker_threads')

const runWorker = (workerData) => {
    return new Promise((resolve, reject) => {
        // 引入 workerExample.js `工作线程`脚本文件
        const worker = new Worker('./workerExample.js', { workerData });
        worker.on('message', resolve);
        worker.on('error', reject);
        worker.on('exit', (code) => {
            if (code !== 0)
                reject(new Error(`stopped with  ${code} exit code`));
        })
    })
}

const main = async () => {
    const result = await runWorker('hello worker threads')
    console.log(result);
}

main().catch(err => console.error(err))
控制台命令列執行:
node main.js

輸出:

{ welcome: 'hello worker threads' }

worker_threads 運算斐波那契數列

在本節中,讓我們看一下CPU密集型範例,產生斐波那契數列

如果在沒有工作執行緒的情況下完成此任務,則會隨著nth

期限的增加而阻塞主執行緒。

建立工作執行緒

腳本檔案

worker.js

const {parentPort, workerData} = require("worker_threads");

parentPort.postMessage(getFibonacciNumber(workerData.num))

function getFibonacciNumber(num) {
    if (num === 0) {
        return 0;
    }
    else if (num === 1) {
        return 1;
    }
    else {
        return getFibonacciNumber(num - 1) + getFibonacciNumber(num - 2);
    }
}
建立主執行緒

腳本檔案

main.js :

const {Worker} = require("worker_threads");

let number = 30;

const worker = new Worker("./worker.js", {workerData: {num: number}});

worker.once("message", result => {
    console.log(`${number}th Fibonacci Result: ${result}`);
});

worker.on("error", error => {
    console.log(error);
});

worker.on("exit", exitCode => {
    console.log(`It exited with code ${exitCode}`);
})

console.log("Execution in main thread");
控制台命令列執行:
node main.js

輸出:

Execution in main thread
30th Fibonacci Result: 832040
It exited with code 0

main.js

檔案中,我們從類別的實例會建立一個工作線程,

Worker正如我們在前面的範例中看到的那樣。 為了得到結果,我們監聽 3 個事件,

  • message响应工作线程发出消息。
  • exit工作线程停止执行的情况下触发的事件。
  • error发生错误时触发。

我们在最后一行main.js

console.log("Execution in main thread");

通过控制台的输出可得,主线程并没有被斐波那契数列运算执行而阻塞。

因此,只要在工作线程中处理 CPU 密集型任务,我们就可以继续处理其他任务而不必担心阻塞主线程。

结论

Node.js 在处理 CPU 密集型任务时一直因其性能而受到批评。通过有效地解决这些缺点,工作线程的引入提高了 Node.js 的功能。

有关worker_threads的更多信息,请在此处访问其官方文档。

思考

文章结束前留下思考,后续会在评论区做补充,欢迎一起讨论。

  • worker_threads线程空闲时候会被回收吗?
  • worker_threads共享内存如何使用?
  • 既然说到线程,那么应该有线程池?

更多node相关知识,请访问:nodejs 教程

以上是聊聊Node.js + worker_threads如何實作多執行緒? (詳解)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除