這篇文章主要介紹“Node.js + worker_threads怎么實現(xiàn)多線程”的相關(guān)知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Node.js + worker_threads怎么實現(xiàn)多線程”文章能幫助大家解決問題。
讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價值的長期合作伙伴,公司提供的服務(wù)項目有:域名申請、網(wǎng)絡(luò)空間、營銷軟件、網(wǎng)站建設(shè)、銅陵網(wǎng)站維護、網(wǎng)站推廣。
通常情況下,Node.js
被認(rèn)為是單線程。由主線程去按照編碼順序一步步執(zhí)行程序代碼,一旦遇到同步代碼阻塞,主線程就會被占用,后續(xù)的程序代碼的執(zhí)行都會被卡住。沒錯Node.js
的單線程指的是主線程是"單線程"。
為了解決單線程帶來的問題,本文的主角worker_threads
出現(xiàn)了。worker_threads
首次在Node.js v10.5.0
作為實驗性功能出現(xiàn),需要命令行帶上--experimental-worker
才能使用。直到v12.11.0
穩(wěn)定版才能正式使用。
本文將會介紹worker_threads
的使用方式,以及利用worker_threads
執(zhí)行斐波那契數(shù)列作為實踐例子。
閱讀并食用本文,需要先具備:
安裝了 Node.js v12.11.0
及以上版本
掌握 JavaScript 同步和異步編程的基礎(chǔ)知識
掌握 Node.js 的工作原理
worker_threads
模塊允許使用并行執(zhí)行 JavaScript 的線程。
工作線程對于執(zhí)行 CPU 密集型的 JavaScript 操作很有用。 它們對 I/O 密集型的工作幫助不大。 Node.js 內(nèi)置的異步 I/O 操作比工作線程更高效。
與 child_process
或 cluster
不同,worker_threads
可以共享內(nèi)存。 它們通過傳輸 ArrayBuffer
實例或共享 SharedArrayBuffer
實例來實現(xiàn)。
由于以下特性,worker_threads
已被證明是充分利用CPU性能的最佳解決方案:
它們運行具有多個線程的單個進(jìn)程。
每個線程執(zhí)行一個事件循環(huán)。
每個線程運行單個 JS 引擎實例。
每個線程執(zhí)行單個 Nodejs 實例。
worker_threads
通過執(zhí)行主線程
指定的腳本文件
來工作。每個線程都在與其他線程隔離的情況下執(zhí)行。但是,這些線程可以通過消息通道來回傳遞消息。
主線程
使用worker.postMessage()
函數(shù)使用消息通道,而工作線程
使用parentPort.postMessage()
函數(shù)。
通過官方示例代碼加強了解:
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)); }
上述代碼主線程
與工作線程
都使用同一份文件作為執(zhí)行腳本(__filename
為當(dāng)前執(zhí)行文件路徑),通過isMainThread
來區(qū)分主線程
與工作線程
運行時邏輯。當(dāng)模塊對外暴露方法parseJSAsync
被調(diào)用時候,都將會衍生子工作線程去執(zhí)行調(diào)用parse
函數(shù)。
在本節(jié)使用具體例子介紹worker_threads
的使用
創(chuàng)建工作線程
腳本文件workerExample.js
:
const { workerData, parentPort } = require('worker_threads') parentPort.postMessage({ welcome: workerData })
創(chuàng)建主線程
腳本文件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))
控制臺命令行執(zhí)行:
node main.js
輸出:
{ welcome: 'hello worker threads' }
在本節(jié)中,讓我們看一下 CPU 密集型示例,生成斐波那契數(shù)列。
如果在沒有工作線程的情況下完成此任務(wù),則會隨著nth
期限的增加而阻塞主線程。
創(chuàng)建工作線程
腳本文件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); } }
創(chuàng)建主線程
腳本文件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");
控制臺命令行執(zhí)行:
node main.js
輸出:
Execution in main thread 30th Fibonacci Result: 832040 It exited with code 0
在main.js
文件中,我們從類的實例創(chuàng)建一個工作線程,Worker
正如我們在前面的示例中看到的那樣。
為了得到結(jié)果,我們監(jiān)聽 3 個事件,
message
響應(yīng)工作線程
發(fā)出消息。
exit
在工作線程
停止執(zhí)行的情況下觸發(fā)的事件。
error
發(fā)生錯誤時觸發(fā)。
我們在最后一行main.js
,
console.log("Execution in main thread");
通過控制臺的輸出可得,主線程
并沒有被斐波那契數(shù)列運算執(zhí)行而阻塞。
因此,只要在工作線程
中處理 CPU 密集型任務(wù),我們就可以繼續(xù)處理其他任務(wù)而不必?fù)?dān)心阻塞主線程。
關(guān)于“Node.js + worker_threads怎么實現(xiàn)多線程”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識,可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點。