站長(zhǎng)資訊網(wǎng)
        最全最豐富的資訊網(wǎng)站

        聊聊Node.js + worker_threads如何實(shí)現(xiàn)多線程?(詳解)

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

        聊聊Node.js + worker_threads如何實(shí)現(xiàn)多線程?(詳解)

        通常情況下,Node.js被認(rèn)為是單線程。由主線程去按照編碼順序一步步執(zhí)行程序代碼,一旦遇到同步代碼阻塞,主線程就會(huì)被占用,后續(xù)的程序代碼的執(zhí)行都會(huì)被卡住。沒(méi)錯(cuò)Node.js的單線程指的是主線程是"單線程"。

        為了解決單線程帶來(lái)的問(wèn)題,本文的主角worker_threads出現(xiàn)了。worker_threads首次在Node.js v10.5.0作為實(shí)驗(yàn)性功能出現(xiàn),需要命令行帶上--experimental-worker才能使用。直到v12.11.0穩(wěn)定版才能正式使用。

        本文將會(huì)介紹worker_threads的使用方式,以及利用worker_threads執(zhí)行斐波那契數(shù)列作為實(shí)踐例子。

        先決條件

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

        • 安裝了 Node.js v12.11.0 及以上版本
        • 掌握 JavaScript 同步和異步編程的基礎(chǔ)知識(shí)
        • 掌握 Node.js 的工作原理

        worker_threads 介紹

        worker_threads 模塊允許使用并行執(zhí)行 JavaScript 的線程。

        工作線程對(duì)于執(zhí)行 CPU 密集型的 JavaScript 操作很有用。 它們對(duì) I/O 密集型的工作幫助不大。 Node.js 內(nèi)置的異步 I/O 操作比工作線程更高效。

        child_processcluster 不同,worker_threads 可以共享內(nèi)存。 它們通過(guò)傳輸 ArrayBuffer 實(shí)例或共享 SharedArrayBuffer 實(shí)例來(lái)實(shí)現(xiàn)。

        由于以下特性,worker_threads已被證明是充分利用CPU性能的最佳解決方案:

        • 它們運(yùn)行具有多個(gè)線程的單個(gè)進(jìn)程。

        • 每個(gè)線程執(zhí)行一個(gè)事件循環(huán)。

        • 每個(gè)線程運(yùn)行單個(gè) JS 引擎實(shí)例。

        • 每個(gè)線程執(zhí)行單個(gè) Nodejs 實(shí)例。

        worker_threads 如何工作

        worker_threads通過(guò)執(zhí)行主線程指定的腳本文件來(lái)工作。每個(gè)線程都在與其他線程隔離的情況下執(zhí)行。但是,這些線程可以通過(guò)消息通道來(lái)回傳遞消息。

        主線程使用worker.postMessage()函數(shù)使用消息通道,而工作線程使用parentPort.postMessage()函數(shù)。

        通過(guò)官方示例代碼加強(qiáng)了解:

        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í)行文件路徑),通過(guò)isMainThread來(lái)區(qū)分主線程工作線程運(yùn)行時(shí)邏輯。當(dāng)模塊對(duì)外暴露方法parseJSAsync被調(diào)用時(shí)候,都將會(huì)衍生子工作線程去執(zhí)行調(diào)用parse函數(shù)。

        worker_threads 具體使用

        在本節(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))

        控制臺(tái)命令行執(zhí)行:

        node main.js

        輸出:

        { welcome: 'hello worker threads' }

        worker_threads 運(yùn)算斐波那契數(shù)列

        在本節(jié)中,讓我們看一下 CPU 密集型示例,生成斐波那契數(shù)列。

        如果在沒(méi)有工作線程的情況下完成此任務(wù),則會(huì)隨著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");

        控制臺(tái)命令行執(zhí)行:

        node main.js

        輸出:

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

        main.js文件中,我們從類的實(shí)例創(chuàng)建一個(gè)工作線程,Worker正如我們?cè)谇懊娴氖纠锌吹降哪菢印?/p>

        為了得到結(jié)果,我們監(jiān)聽(tīng) 3 個(gè)事件,

        • message響應(yīng)工作線程發(fā)出消息。
        • exit工作線程停止執(zhí)行的情況下觸發(fā)的事件。
        • error發(fā)生錯(cuò)誤時(shí)觸發(fā)。

        我們?cè)谧詈笠恍?code>main.js,

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

        通過(guò)控制臺(tái)的輸出可得,主線程并沒(méi)有被斐波那契數(shù)列運(yùn)算執(zhí)行而阻塞。

        因此,只要在工作線程中處理 CPU 密集型任務(wù),我們就可以繼續(xù)處理其他任務(wù)而不必?fù)?dān)心阻塞主線程。

        結(jié)論

        Node.js 在處理 CPU 密集型任務(wù)時(shí)一直因其性能而受到批評(píng)。通過(guò)有效地解決這些缺點(diǎn),工作線程的引入提高了 Node.js 的功能。

        有關(guān)worker_threads

        贊(0)
        分享到: 更多 (0)
        網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)
        主站蜘蛛池模板: 97久久精品人人澡人人爽| 亚洲天堂久久精品| 粉嫩精品美女国产在线观看| 久久精品水蜜桃av综合天堂| 精品一区二区三区色花堂| 久久精品国产精品青草| 久久香蕉超碰97国产精品| 婷婷国产成人精品一区二| 精品人妻伦一二三区久久| 国内精品免费视频精选在线观看| 青青草原精品99久久精品66| 亚洲午夜精品久久久久久app| 精品欧美激情在线看| 国产精品高清在线观看| 你懂的国产精品| 99久久免费国产精精品| 精品国产AV一区二区三区 | 精品国产一区二区三区无码 | 人妻精品久久久久中文字幕一冢本| 日韩精品欧美激情国产一区| 久久精品国产一区二区三区| 国产精品JIZZ在线观看老狼| 大胸国产精品视频| 国产精品内射久久久久欢欢| 国产精品乱伦| 国产区精品高清在线观看| 国产亚洲精品a在线观看| 国产精品免费大片一区二区| 国产精品视频一区二区三区不卡| 国产福利电影一区二区三区,亚洲国模精品一区 | www.久久精品| 国产综合色在线精品| 久久成人国产精品| 少妇人妻无码精品视频app| 一本一道久久a久久精品综合| 亚洲精品无码久久不卡| 亚洲午夜国产精品无码老牛影视| 最新国产精品无码| 久久综合国产乱子伦精品免费| 精品无码一区二区三区爱欲九九 | 国产精品视频一区二区三区经|