如何理解Node.js的单线程?

我们常说, Node.js 是单线程的,这句话对新人有很大的误导作用。首先要明确:Node.js 程序并非「单线程」,证明代码如下:

let end = Date.now() + 1000 * 10
for (; Date.now() < end;) {}

运行之后,到活动监视器中搜索 Node,结果如下:

活动监视器中搜索 Node
看到没,一个 Node 程序有 7 个线程。到这里,你可能会很困惑,这究竟是怎么回事?其实正确的说法应该是:

单线程的指的是 JavaScript 的执行是单线程的,但 Javascript 的宿主环境并非单线程。

怎么理解这句话呢?其实当你用 node xxx.js 运行程序的时候,操作系统会启动下面 7 个线程:

  • 1 个 Javascript 主线程用于执行用户代码
  • 1 个 watchdog 监控线程用于处理调试信息
  • 1 个 v8 task scheduler 线程用于调度任务优先级,加速延迟敏感任务执行
  • 4 个 v8 线程用来执行代码调优与 GC 等后台任务

所以说,我们的 Node 程序中包含的线程实际上是:JavaScript 的宿主环境需要的线程 + JavaScript 的执行线程。

到这里是不是有种豁然开朗的感觉了?别急,我再加一行代码:

require('fs').readFile(require.main.filename, () => {})
let end = Date.now() + 1000 * 10
for (; Date.now() < end;) {}

这个时候活动监视器的结果如下:

如何理解Node.js的单线程?

WTF?怎么多了 4 个线程?这是怎么回事?其实原因就出现在第一行代码上:

require('fs').readFile(require.main.filename, () => {})

这行代码是异步操作,而 Node 是「异步非阻塞」的语言,如果还是 7 个线程的话,就意味着 JavaScript 主线程也要执行 I/O 操作,从而造成了阻塞。所以 libuv 创建了线程池,默认情况下线程池里面有 4 个线程,所以我们看到的结果是 11。

也就是说,代码里面那些异步操作,全部由线程池来接管,JavaScript 主线程不参与进去,只是执行同步代码而已。下面是 Node 进程结构图:

Node 进程结构图
如果全部是同步代码,那么只会开启 7 个线程,如果存在异步 I/O 操作,则默认会开启 11 个线程。为什么说是「默认」呢?因为 uv_thread_pool 的容量是可以改的,只要设置环境变量 UV_THREADPOOL_SIZE 即可。例如下面的代码把线程池的容量改成 1:

process.env.UV_THREADPOOL_SIZE = 1
require('fs').readFile(require.main.filename, () => {})
let end = Date.now() + 1000 * 10
for (; Date.now() < end;) {}

这个时候就只有 8 个线程了:

如何理解Node.js的单线程?
所以,Node.js 程序并非单线程,只不过主线程是单线程的,所有的异步 I/O操作由 libuv 的线程池中的线程进行处理,然后把运行结果通过回调的方式通知到主线程。

1. 本站所有免费资源来源于用户上传和网络,因此不包含技术服务请大家谅解!如有侵权请邮件联系客服!
2. 本站不保证所提供下载的免费资源的准确性、安全性和完整性,免费资源仅供下载学习之用!如有链接无法下载、失效,请联系客服处理!
3. 您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容资源!如用于商业或者非法用途,与本站无关,一切后果请用户自负!
4. 如果您也有好的资源或技术教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
5. 加入前端开发QQ群:565733884,我们大家一起来交流技术!
码云笔记 » 如何理解Node.js的单线程?

发表评论

提供最优质的资源集合

立即查看 了解详情