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

        聊聊利用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號
        主站蜘蛛池模板: 午夜精品乱人伦小说区| 精品无码人妻夜人多侵犯18| 国产精品亚洲一区二区三区在线| 国产精品成人观看视频| 精品视频一区二区三三区四区| 久久精品国产72国产精福利| 欧美日韩在线亚洲国产精品| 久久精品www人人爽人人| 日本五区在线不卡精品| 国产精品福利片免费看| 国99精品无码一区二区三区| 亚洲AV无码精品色午夜果冻不卡| 免费观看四虎精品成人| 国产精品成人观看视频| 亚洲一区精品中文字幕| 极品精品国产超清自在线观看| 日本VA欧美VA精品发布| 日韩在线精品一二三区| 久久夜色精品国产www| 国产亚洲精品自在线观看| 香蕉国产精品频视| 国产精品久久久久影院嫩草| 人妻少妇精品无码专区二区| 亚洲欧美精品一区久久中文字幕| 久久国产综合精品五月天| 国产精品亚洲产品一区二区三区 | 国产精品成人小电影在线观看| 国产精品国产三级国产专播| 成人精品在线视频| 国产成人精品久久二区二区| 国产cosplay精品视频| 国产精品视频分类一区| 精品三级在线观看| 欧美精品在线一区| 国产A∨免费精品视频| 国产精品无码素人福利不卡| 精品亚洲一区二区三区在线观看| 久久国产精品视频| 亚洲精品成人片在线观看| 亚洲动漫精品无码av天堂| 久久99精品国产麻豆|