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

如何理解 Node.js 不是完全的單線程的程序(淺析)

為什么說 Node.js 不是完全的單線程?如何理解?下面本篇文章就來帶大家探討一下,希望對大家有所幫助!

如何理解 Node.js 不是完全的單線程的程序(淺析)

相信大家都知道 node 是一個(gè)單線程程序,使用了 Event Loop 可以做到多并發(fā)??上н@是不完全正確的。

那么為什么說 Node.js 不是完全的單線程的程序呢?

Node.js 是單線程的程序*

所有我們自己寫的 Javsacript,V8, event loop都跑在同一個(gè)線程里面,也就是 main thrad。

哎嗨,這不正說明 node 是單線程的嗎?

但是也許你不知道 node 有很多模塊背后都是 C++ code。

雖然 node 沒有給使用者暴露控制 thread 的權(quán)限,但是 C++ 是可以使用多線程的。

那么什么時(shí)候 node 會(huì)使用多線程呢?

  • 如果一個(gè) node 方法,背后調(diào)用C++的同步方法,那么都是跑在 main thread 里面的。

  • 如果一個(gè) node 方法,背后調(diào)用C++的異步方法,有時(shí)候不是跑在 main thread 里面的。

Talk is cheap, show me the code.

同步方法,跑在 main thread 里面

這里 crypto 相關(guān)模塊,很多是 C++ 寫的。下面一段程序是計(jì)算hash的函數(shù),一般用來存儲(chǔ)密碼。

import { pbkdf2Sync } from "crypto"; const startTime = Date.now(); let index = 0; for (index = 0; index < 3; index++) {     pbkdf2Sync("secret", "salt", 100000, 64, "sha512");     const endTime = Date.now();     console.log(`${index} time, ${endTime - startTime}`); } const endTime = Date.now(); console.log(`in the end`);

輸出的時(shí)間,

0 time, 44  1 time, 90 2 time, 134 in the end

可以看到每次大概都是花費(fèi)~45ms,代碼 main thread 上順序執(zhí)行。

注意最后的輸出是誰? 注意這里一次 hash 在我的 cpu 需要~45ms。

異步 pbkdf2 方法,不跑在 main thread 里面

import { cpus } from "os"; import { pbkdf2 } from "crypto"; console.log(cpus().length); let startTime = console.time("time-main-end"); for (let index = 0; index < 4; index++) {     startTime = console.time(`time-${index}`);     pbkdf2("secret", `salt${index}`, 100000, 64, "sha512", (err, derivedKey) => {         if (err) throw err;         console.timeEnd(`time-${index}`);     }); } console.timeEnd("time-main-end");

輸出的時(shí)間,

time-main-end: 0.31ms time-2: 45.646ms time-0: 46.055ms time-3: 46.846ms time-1: 47.159ms

這里看到,main thread 早早結(jié)束,然而每次計(jì)算的時(shí)間都是45ms,要知道一個(gè) cpu 計(jì)算 hash 的時(shí)間是45ms,這里 node 絕對使用了多個(gè)線程進(jìn)行hash計(jì)算。

如果我這里把調(diào)用次數(shù)改成10次,那么時(shí)間如下,可以看到隨著CPU核數(shù)的用完,時(shí)間也在增加。再一次證明node 絕對使用了多個(gè)線程進(jìn)行hash計(jì)算。

time-main-end: 0.451ms time-1: 44.977ms time-2: 46.069ms time-3: 50.033ms time-0: 51.381ms time-5: 96.429ms // 注意這里,從第五次時(shí)間開始增加了 time-7: 101.61ms time-4: 113.535ms time-6: 121.429ms time-9: 151.035ms time-8: 152.585ms

雖然這里證明了,node絕對啟用了多線程。但是有一點(diǎn)點(diǎn)小小的問題?我的電腦的CPU是AMD R5-5600U,有6個(gè)核心12線程啊。但是為什么時(shí)間是從第五次開始增加的呢,node沒有完全利用我的CPU?。?/p>

原因是什么呢?

Node 使用了預(yù)定義的線程池,這個(gè)線程池的大小默認(rèn)是4.

export UV_THREADPOOL_SIZE=6

讓我們在看一個(gè)例子,

HTTP request

import { request } from "https"; const options = {   hostname: "www.baidu.com",   port: 443,   path: "/img/PC_7ac6a6d319ba4ae29b38e5e4280e9122.png",   method: "GET", };  let startTime = console.time(`main`);  for (let index = 0; index < 15; index++) {   startTime = console.time(`time-${index}`);   const req = request(options, (res) => {     console.log(`statusCode: ${res.statusCode}`);     console.timeEnd(`time-${index}`);     res.on("data", (d) => {       // process.stdout.write(d);     });   });    req.on("error", (error) => {     console.error(error);   });    req.end(); }  console.timeEnd("main");
main: 13.927ms time-2: 83.247ms time-4: 89.641ms time-3: 91.497ms time-12: 91.661ms time-5: 94.677ms ..... time-8: 134.026ms time-1: 143.906ms time-13: 140.914ms time-10: 144.088ms

這里主程序也早早結(jié)束了,這里我啟動(dòng) http request 去下載15次圖片,他們花費(fèi)的時(shí)間并沒有成倍增加,似乎不受限于線程池/cpu的影響。

為什么啊??Node 到底有沒有在使用線程池???

如果 Node 背后的 C++ 的異步方法,首先會(huì)嘗試是否有內(nèi)核異步支持,比如這里網(wǎng)絡(luò)請是使用 epoll (Linux),如果內(nèi)核沒有提供異步方式,Node才會(huì)使用自己的線程池。。

所以 http 請求雖然是異步,不過是由內(nèi)核實(shí)現(xiàn)的,等到內(nèi)核完成后,會(huì)通知C++, C++會(huì)通知給 main thread 處理callback。

那么 Node 哪些異步方法會(huì)使用線程池呢?哪些不會(huì)呢?

  • 原生 Kernal Async

    • TCP/UDP server client
    • Unix Domain Sockets (IPC)
    • pipes
    • dns.resolveXXX
    • tty input(stdin etc)
    • Unix signals
    • Child process
  • Thread pool

    • fs.*
    • dns.lookup
    • pipe (edge case)

這也是大部分 Node 優(yōu)化的切入點(diǎn)。

但是這些怎么和最重要的 Event Loop 結(jié)合起來呢?

Event Loop

相信大家都對 Event loop 非常熟悉了。Event loop 好比一個(gè)分發(fā)員,

  • 如果是遇到普通 javascript 程序或者是 callback,交給 V8 處理。

  • 如果遇到同步方法后背是 C++ 寫的,交給C++,跑在 main thread。

  • 如果遇到異步方法后背是 C++ 寫的,如果有內(nèi)核異步支持,從main thread 交給內(nèi)核處理。

  • 如果是異步方法后背是 C++ 寫的,如果沒有內(nèi)核異步支持,從 main thread 交給 thread pool。

  • thread pool 和內(nèi)核有結(jié)果都會(huì)把結(jié)果返回 event loop,如果注冊的有 javascript callback,就交給V8進(jìn)行處理。

然后如此循環(huán),直到?jīng)]有東西可以處理。

所以 Node 不完全是單線程程序。

贊(0)
分享到: 更多 (0)
網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號
日韩精品免费在线视频| 精品午夜福利无人区乱码一区| 欲帝精品福利视频导航| 精品久久久久久中文| 精品视频一区二区三区四区| 538精品视频在线观看| 国产精品久久久久AV福利动漫| 国产日韩久久久精品影院首页 | 日本午夜精品视频在线观看| 91在线亚洲精品专区| 久久国产精品免费专区| 国产精品无码成人午夜电影| 99re6在线视频精品免费| 国产精品美女久久久久av爽| 一色屋精品视频任你曰| 秋霞日韩久久理论电影| 2015日韩永久免费视频播放| 国产日韩久久久精品影院首页| 国产精品爆乳在线播放第一人称| 黑巨人与欧美精品一区| 亚洲精品国产摄像头| 亚洲精品无播放器在线播放| 2021精品国产综合久久| 国产精品午夜剧场| 99re热久久这里只有精品首页| 精品久久久久久无码专区不卡| 91在线手机精品免费观看| 精品亚洲成AV人在线观看| 99re5精品视频在线观看| 热re99久久6国产精品免费| 无码人妻精品一区二区三区在线 | 思思91精品国产综合在线| 蜜臀91精品国产免费观看| 热久久99精品这里有精品| 国产精品黄大片在线播放| 国产精品综合视频| 国产在线麻豆精品| 国产日韩精品SUV| 日韩一级视频免费观看| 丰满人妻熟妇乱又伦精品软件| 国产精品麻豆VA在线播放|