站長資訊網
        最全最豐富的資訊網站

        聊聊利用Node.js 的多線程能力怎么做異步計算

        怎么做異步計算?下面本篇文章給大家介紹一下利用瀏覽器和 Node.js 的多線程能力做異步計算的方法,希望對大家有所幫助!

        聊聊利用Node.js 的多線程能力怎么做異步計算

        都說 Node.js 可以實現高性能的服務器,那什么是高性能呢?

        所有的軟件代碼最終都是通過 CPU 來跑的,能不能把 CPU 高效利用起來是區分性能高低的標志,也就是說不能讓它空轉。【推薦學習:《nodejs 教程》】

        那什么時候會空轉呢?

        • 當程序在進行網絡和磁盤的 IO 的時候,這時候 CPU 是空閑的,也就是在空轉。
        • 多核 CPU 可以同時跑多個程序,如果只利用了其中一核,那么其他核也是在空轉。

        所以,要想達到高性能,就要解決這兩個問題。

        操作系統提供了線程的抽象,對應代碼不同的執行分支,都是可以同時上不同的 CPU 跑的,這是利用好多核 CPU 性能的方式。

        而如果有的線程在進行 IO 了,也就是要阻塞的等待讀寫完成,這種是比較低效的方式,所以操作系統實現了 DMA 的機制,就是設備控制器,由硬件來負責從設備到內存的搬運,在搬完了告訴 CPU 一聲。這樣當有的線程在 IO 的時候就可以把線程暫停掉,等收到 DMA 運輸數據完成的通知再繼續跑。

        多線程、DMA,這是利用好多核 CPU 優勢、解決 CPU 阻塞等 IO 的問題的操作系統提供的解決方案。

        而各種編程語言對這種機制做了封裝,Node.js 也是,Node.js 之所以是高性能,就是因為異步 IO 的設計。

        Node.js 的異步 IO 的實現在 libuv,基于操作系統提供的異步的系統調用,這種一般是硬件級別的異步,比如 DMA 搬運數據。但是其中有一些同步的系統調用,通過 libuv 封裝以后也會變成異步的,這是因為 libuv 內有個線程池,來執行這些任務,把同步的 API 變成異步的。這個線程池的大小可以通過 UV_THREADPOOL_SIZE 的環境變量設置,默認是 4。

        聊聊利用Node.js 的多線程能力怎么做異步計算

        我們在代碼里調用的異步 API,很多都是通過線程來實現的。

        比如:

        const fsPromises = require('fs').promises;  const data = await fsPromises.readFile('./filename');

        但是,這種異步 API 只解決了 IO 的問題,那如何利用多核 CPU 的優勢來做計算呢?

        Node.js 在 10.5 實驗性的引入(在 12 正式引入)了 worker_thread 模塊,可以創建線程,最終用多個 CPU 跑,這是利用多核 CPU 的做計算的方式。

        異步 API 可以利用多線程做 IO,而 worker_thread 可以創建線程做計算,用于不同的目的。

        要聊清楚 worker_thread,還得從瀏覽器的 web worker 聊起。

        瀏覽器的 web worker

        瀏覽器也同樣面臨不能利用多核 CPU 做計算的問題,所以 html5 引入了 web worker,可以通過另一個線程做計算。

        <!DOCTYPE html> <html> <head></head> <body>     <script>         (async function () {             const res = await runCalcWorker(2, 3, 3, 3);             console.log(res);         })();          function runCalcWorker(...nums) {             return new Promise((resolve, reject) => {                 const calcWorker = new Worker('./webWorker.js');                 calcWorker.postMessage(nums)                 calcWorker.onmessage = function (msg) {                     resolve(msg.data);                 };                 calcWorker.onerror = reject;             });         }     </script>  </body> </html>

        我們創建一個 Worker 對象,指定跑在另一個線程的 js 代碼,然后通過 postMessage 傳遞消息給它,通過 onMessage 接收消息。這個過程也是異步的,我們進一步把它封裝成了 promise。

        然后在 webWorker.js 里面接收數據,做計算,之后通過 postMessage 傳回結果。

        // webWorker.js onmessage = function(msg) {     if (Array.isArray(msg.data)) {         const res = msg.data.reduce((total, cur) => {             return total += cur;         }, 0);         postMessage(res);     } }

        這樣,我們就利用了另一個 CPU 核來跑了這段計算,對寫代碼來說和普通的異步代碼沒啥區別。但這個異步實際上不是 IO 的異步,而是計算的異步。

        Node.js 的 worker thread 和 web worker 類似,我甚至懷疑 worker thread 的名字就是受 web worker 影響的。

        Node.js 的 worker thread

        把上面那段異步計算的邏輯在 Node.js 里面實現話,是這樣的:

        const runCalcWorker = require('./runCalcWorker');  (async function () {     const res = await runCalcWorker(2, 3, 3, 3);     console.log(res); })();

        以異步的方式調用,因為異步計算和異步 IO 在使用方式上沒啥區別。

        // runCalcWorker.js const  { Worker } = require('worker_threads');  module.exports = function(...nums) {     return new Promise(function(resolve, reject) {         const calcWorker = new Worker('./nodeWorker.js');         calcWorker.postMessage(nums);          calcWorker.on('message', resolve);         calcWorker.on('error', reject);     }); }

        然后異步計算的實現是通過創建 Worker 對象,指定在另一個線程跑的 JS,然后通過 postMessage 傳遞消息,通過 message 接收消息。這個和 web worker 很類似。

        // nodeWorker.js const {     parentPort } = require('worker_threads');  parentPort.on('message', (data) => {     const res = data.reduce((total, cur) => {         return total += cur;     }, 0);     parentPort.postMessage(res); });

        在具體執行計算的 nodeWorker.js 里面,監聽 message 消息,然后進行計算,通過 parentPost.postMessage 傳回數據。

        對比下 web worker,你會發現特別的像。所以,我覺得 Node.js 的 worker thread 的 api 是參考 web worker 來設計的。

        但是,其實 worker thread 也支持在創建的時候就通過 wokerData 傳遞數據:

        const  { Worker } = require('worker_threads');  module.exports = function(...nums) {     return new Promise(function(resolve, reject) {         const calcWorker = new Worker('./nodeWorker.js', {             workerData: nums         });         calcWorker.on('message', resolve);         calcWorker.on('error', reject);     }); }

        然后 worker 線程里通過 workerData 來取:

        const {     parentPort,     workerData } = require('worker_threads');  const data = workerData; const res = data.reduce((total, cur) => {     return total += cur; }, 0); parentPort.postMessage(res);

        因為有個傳遞消息的機制,所以要做序列化和反序列化,像函數這種無法被序列化的數據就無法傳輸了。這也是 worker thread 的特點。

        Node.js 的 worker thread 和 瀏覽器 web woker 的對比

        從使用上來看,都可以封裝成普通的異步調用,和其他異步 API 用起來沒啥區別。

        都要經過數據的序列化反序列化,都支持 postMessage、onMessage 來收發消息。

        除了 message,Node.js 的 worker thread 支持傳遞數據的方式

        贊(0)
        分享到: 更多 (0)
        網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號
        主站蜘蛛池模板: 国产精品高清一区二区三区不卡| 高清在线亚洲精品国产二区| 欧美精品videosse精子| 国产精品亚洲二区在线观看| 国产精品人人爽人人做我的可爱| 日本精品久久久久久久久免费| 51精品资源视频在线播放| 精品国际久久久久999波多野| 亚洲欧美日韩国产成人精品影院 | 久久精品人人做人人爽电影| 亚洲国产精品一区二区第一页 | 亚洲国产精品无码久久青草 | 亚洲AV无码久久精品色欲| 国产精品自产拍在线18禁| 欧美精品在线一区二区三区| 99精品国产一区二区三区2021| 久久亚洲精品国产精品| 自拍偷自拍亚洲精品被多人伦好爽| 精品久久久久久国产免费了| 999久久久国产精品| 国产精品久久久天天影视| 国产精品国产三级国产普通话| 无码精品久久久久久人妻中字| 亚洲av无码成人精品国产| 久久久久成人精品无码| 国产在AJ精品| 久久精品国产亚洲精品| 精品一区二区三区高清免费观看| 国产精品九九久久免费视频 | 日韩精品中文字幕第2页| 精品伦精品一区二区三区视频| 国产精品视频全国免费观看| 99久久精品国产综合一区| 午夜精品福利视频| 中文字幕成人精品久久不卡| 午夜精品美女写真福利| 国产成人亚洲精品影院| 国产精品亚洲精品日韩已方| 国产三级精品三级| 精品无码三级在线观看视频| 欧美精品国产一区二区三区|