Node.js是不是单线程,怎么理解
Admin 2022-07-15 群英技术资讯 1007 次浏览
这篇文章给大家介绍了“Node.js是不是单线程,怎么理解”的相关知识,讲解详细,步骤过程清晰,有一定的借鉴学习价值,因此分享给大家做个参考,感兴趣的朋友接下来一起跟随小编看看吧。
相信大家都知道 node 是一个单线程程序,使用了 Event Loop 可以做到多并发。可惜这是不完全正确的。
那么为什么说 Node.js 不是完全的单线程的程序呢?
所有我们自己写的 Javsacript,V8, event loop都跑在同一个线程里面,也就是 main thrad。
哎嗨,这不正说明 node 是单线程的吗?
但是也许你不知道 node 有很多模块背后都是 C++ code。
虽然 node 没有给使用者暴露控制 thread 的权限,但是 C++ 是可以使用多线程的。
那么什么时候 node 会使用多线程呢?
如果一个 node 方法,背后调用C++的同步方法,那么都是跑在 main thread 里面的。
如果一个 node 方法,背后调用C++的异步方法,有时候不是跑在 main thread 里面的。
Talk is cheap, show me the code.
这里 crypto 相关模块,很多是 C++ 写的。下面一段程序是计算hash的函数,一般用来存储密码。
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`);输出的时间,
0 time, 44 1 time, 90 2 time, 134 in the end
可以看到每次大概都是花费~45ms,代码 main thread 上顺序执行。
注意最后的输出是谁? 注意这里一次 hash 在我的 cpu 需要~45ms。
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");输出的时间,
time-main-end: 0.31ms time-2: 45.646ms time-0: 46.055ms time-3: 46.846ms time-1: 47.159ms
这里看到,main thread 早早结束,然而每次计算的时间都是45ms,要知道一个 cpu 计算 hash 的时间是45ms,这里 node 绝对使用了多个线程进行hash计算。
如果我这里把调用次数改成10次,那么时间如下,可以看到随着CPU核数的用完,时间也在增加。再一次证明node 绝对使用了多个线程进行hash计算。
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 // 注意这里,从第五次时间开始增加了 time-7: 101.61ms time-4: 113.535ms time-6: 121.429ms time-9: 151.035ms time-8: 152.585ms
虽然这里证明了,node绝对启用了多线程。但是有一点点小小的问题?我的电脑的CPU是AMD R5-5600U,有6个核心12线程啊。但是为什么时间是从第五次开始增加的呢,node没有完全利用我的CPU啊?
原因是什么呢?
Node 使用了预定义的线程池,这个线程池的大小默认是4.
export UV_THREADPOOL_SIZE=6
让我们在看一个例子,
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
这里主程序也早早结束了,这里我启动 http request 去下载15次图片,他们花费的时间并没有成倍增加,似乎不受限于线程池/cpu的影响。
为什么啊??Node 到底有没有在使用线程池啊?
如果 Node 背后的 C++ 的异步方法,首先会尝试是否有内核异步支持,比如这里网络请是使用 epoll (Linux),如果内核没有提供异步方式,Node才会使用自己的线程池。。
所以 http 请求虽然是异步,不过是由内核实现的,等到内核完成后,会通知C++, C++会通知给 main thread 处理callback。
那么 Node 哪些异步方法会使用线程池呢?哪些不会呢?
原生 Kernal Async
Thread pool
这也是大部分 Node 优化的切入点。
但是这些怎么和最重要的 Event Loop 结合起来呢?
相信大家都对 Event loop 非常熟悉了。Event loop 好比一个分发员,
如果是遇到普通 javascript 程序或者是 callback,交给 V8 处理。
如果遇到同步方法后背是 C++ 写的,交给C++,跑在 main thread。
如果遇到异步方法后背是 C++ 写的,如果有内核异步支持,从main thread 交给内核处理。
如果是异步方法后背是 C++ 写的,如果没有内核异步支持,从 main thread 交给 thread pool。
thread pool 和内核有结果都会把结果返回 event loop,如果注册的有 javascript callback,就交给V8进行处理。
然后如此循环,直到没有东西可以处理。
所以 Node 不完全是单线程程序。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
ajax和websocket有哪些不同?对于ajax和websocket的区别,一些朋友比较好奇,其中两者在本质、生命周期、适用范围、发起人和用法这五方面都有区别,接下来我们详细的了解看看。
Node处理CPU密集型任务的方法有哪些?下面本篇文章就来带大家了解一下Node处理CPU密集型任务的方法,希望对大家有所帮助!
本文主要介绍了Vue实现组件间通信的几种方式,不同的场景使用不同的方式,基本满足所有开发场景中的通信需求,感兴趣的可以了解一下
jQuery中的$符号表示什么?日常中我们看到$符号是表示美元的符号,但是在jQuery中$符号是代表获取的意思,是一个回传函数。这篇我们就来了解看看$符号怎么样使用。
这篇文章主要为大家详细介绍了JavaScript实现简单计算器小功能,只学书上的理论是远远不够的,只有在实战中才能获得能力的提升,大家可以在本篇实例中查缺补漏,提升水平
成为群英会员,开启智能安全云计算之旅
立即注册关注或联系群英网络
7x24小时售前:400-678-4567
7x24小时售后:0668-2555666
24小时QQ客服
群英微信公众号
CNNIC域名投诉举报处理平台
服务电话:010-58813000
服务邮箱:service@cnnic.cn
投诉与建议:0668-2555555
Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 ICP核准(ICP备案)粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008