亚洲最大看欧美片,亚洲图揄拍自拍另类图片,欧美精品v国产精品v呦,日本在线精品视频免费

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

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

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

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

    都說 Node.js 可以實(shí)現(xiàn)高性能的服務(wù)器,那什么是高性能呢?

    所有的軟件代碼最終都是通過 CPU 來跑的,能不能把 CPU 高效利用起來是區(qū)分性能高低的標(biāo)志,也就是說不能讓它空轉(zhuǎn)?!就扑]學(xué)習(xí):《nodejs 教程》】

    那什么時(shí)候會(huì)空轉(zhuǎn)呢?

    • 當(dāng)程序在進(jìn)行網(wǎng)絡(luò)和磁盤的 IO 的時(shí)候,這時(shí)候 CPU 是空閑的,也就是在空轉(zhuǎn)。
    • 多核 CPU 可以同時(shí)跑多個(gè)程序,如果只利用了其中一核,那么其他核也是在空轉(zhuǎn)。

    所以,要想達(dá)到高性能,就要解決這兩個(gè)問題。

    操作系統(tǒng)提供了線程的抽象,對(duì)應(yīng)代碼不同的執(zhí)行分支,都是可以同時(shí)上不同的 CPU 跑的,這是利用好多核 CPU 性能的方式。

    而如果有的線程在進(jìn)行 IO 了,也就是要阻塞的等待讀寫完成,這種是比較低效的方式,所以操作系統(tǒng)實(shí)現(xiàn)了 DMA 的機(jī)制,就是設(shè)備控制器,由硬件來負(fù)責(zé)從設(shè)備到內(nèi)存的搬運(yùn),在搬完了告訴 CPU 一聲。這樣當(dāng)有的線程在 IO 的時(shí)候就可以把線程暫停掉,等收到 DMA 運(yùn)輸數(shù)據(jù)完成的通知再繼續(xù)跑。

    多線程、DMA,這是利用好多核 CPU 優(yōu)勢(shì)、解決 CPU 阻塞等 IO 的問題的操作系統(tǒng)提供的解決方案。

    而各種編程語言對(duì)這種機(jī)制做了封裝,Node.js 也是,Node.js 之所以是高性能,就是因?yàn)楫惒?IO 的設(shè)計(jì)。

    Node.js 的異步 IO 的實(shí)現(xiàn)在 libuv,基于操作系統(tǒng)提供的異步的系統(tǒng)調(diào)用,這種一般是硬件級(jí)別的異步,比如 DMA 搬運(yùn)數(shù)據(jù)。但是其中有一些同步的系統(tǒng)調(diào)用,通過 libuv 封裝以后也會(huì)變成異步的,這是因?yàn)?libuv 內(nèi)有個(gè)線程池,來執(zhí)行這些任務(wù),把同步的 API 變成異步的。這個(gè)線程池的大小可以通過 UV_THREADPOOL_SIZE 的環(huán)境變量設(shè)置,默認(rèn)是 4。

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

    我們?cè)诖a里調(diào)用的異步 API,很多都是通過線程來實(shí)現(xiàn)的。

    比如:

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

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

    Node.js 在 10.5 實(shí)驗(yàn)性的引入(在 12 正式引入)了 worker_thread 模塊,可以創(chuàng)建線程,最終用多個(gè) CPU 跑,這是利用多核 CPU 的做計(jì)算的方式。

    異步 API 可以利用多線程做 IO,而 worker_thread 可以創(chuàng)建線程做計(jì)算,用于不同的目的。

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

    瀏覽器的 web worker

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

    <!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>

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

    然后在 webWorker.js 里面接收數(shù)據(jù),做計(jì)算,之后通過 postMessage 傳回結(jié)果。

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

    這樣,我們就利用了另一個(gè) CPU 核來跑了這段計(jì)算,對(duì)寫代碼來說和普通的異步代碼沒啥區(qū)別。但這個(gè)異步實(shí)際上不是 IO 的異步,而是計(jì)算的異步。

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

    Node.js 的 worker thread

    把上面那段異步計(jì)算的邏輯在 Node.js 里面實(shí)現(xiàn)話,是這樣的:

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

    以異步的方式調(diào)用,因?yàn)楫惒接?jì)算和異步 IO 在使用方式上沒啥區(qū)別。

    // 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);     }); }

    然后異步計(jì)算的實(shí)現(xiàn)是通過創(chuàng)建 Worker 對(duì)象,指定在另一個(gè)線程跑的 JS,然后通過 postMessage 傳遞消息,通過 message 接收消息。這個(gè)和 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); });

    在具體執(zhí)行計(jì)算的 nodeWorker.js 里面,監(jiān)聽 message 消息,然后進(jìn)行計(jì)算,通過 parentPost.postMessage 傳回?cái)?shù)據(jù)。

    對(duì)比下 web worker,你會(huì)發(fā)現(xiàn)特別的像。所以,我覺得 Node.js 的 worker thread 的 api 是參考 web worker 來設(shè)計(jì)的。

    但是,其實(shí) worker thread 也支持在創(chuàng)建的時(shí)候就通過 wokerData 傳遞數(shù)據(jù):

    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 來?。?/p>

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

    因?yàn)橛袀€(gè)傳遞消息的機(jī)制,所以要做序列化和反序列化,像函數(shù)這種無法被序列化的數(shù)據(jù)就無法傳輸了。這也是 worker thread 的特點(diǎn)。

    Node.js 的 worker thread 和 瀏覽器 web woker 的對(duì)比

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

    都要經(jīng)過數(shù)據(jù)的序列化反序列化,都支持 postMessage、onMessage 來收發(fā)消息。

    除了 message,Node.js 的 worker thread 支持傳遞數(shù)據(jù)的方式

    贊(0)
    分享到: 更多 (0)
    網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)