为什么Node.js在维护线程池中的线程时会调用单线程?

时间:2018-01-13 15:19:09

标签: node.js event-loop

Node.js维护一个事件循环,但是默认情况下它也有四个复杂请求的线程。当线程池中有更多可用线程时,这是单线程的吗?

此外,复杂任务的事件循环分配的线程是专用线程,那么它与其他多线程概念有何不同?

3 个答案:

答案 0 :(得分:7)

在您所指的上下文中,“单线程”表示您的Javascript作为单个线程运行。 没有两个Javascript同时运行字面或时间切片。这大大简化了Javascript开发,因为不需要为不同的Javascript片段之间共享的Javascript变量进行线程同步,因为只有一段Javascript可以同时运行。

所有这一切,node.js确实使用其实现内部的线程。您提到的默认四个线程用于磁盘I / O的线程池。因为磁盘I / O通常是OS级别的同步操作,它阻塞调用线程和node.js的设计,其中所有I / O操作都应作为异步操作提供,node.js设计者决定实现异步接口通过使用线程池来实现(在本机代码中),fs模块磁盘I / O接口(是的,在某些操作系统中有非阻塞磁盘I / O操作,但node.js设计者决定不使用它们)。这一切都发生在本机代码的掩盖之下,并不影响您的Javascript仅在单个线程中运行的事实。

以下是有关磁盘I / O调用如何在node.js中工作的摘要。我们假设已经有一个打开的文件句柄。

  1. Javascript代码在现有文件句柄上调用fs.write()
  2. fs模块将参数打包到函数中,然后调用本机代码。
  3. 本机代码从线程池获取线程并启动OS调用以将数据写入该文件
  4. 本机代码从函数
  5. 返回
  6. fs模块从fs.write()调用
  7. 返回
  8. Javascript继续执行(fs.write()调用
  9. 后发出的任何声明
  10. 一段时间后,线程上的本机代码fs.write()调用结束。它获取保护事件循环的互斥锁并在事件队列中插入事件。
  11. 当Javascript引擎完成执行它正在运行的任何Javascript流时,它会检查事件队列以查看是否还有其他事件要运行。
  12. 当它在事件队列中找到一个事件时,它会将其从事件队列中删除,并执行与该事件关联的回调,启动一个运行Javascript的新流。
  13. 因为在当前的Javascript流完成执行之前从未对新事件起作用,所以Javascript获取的是事件驱动的单线程性质,即使本机代码线程可用于实现某些库函数。这些线程用于将阻塞操作转换为非阻塞操作,但不会影响Javascript执行本身的单线程。

    这里的关键是node.js是事件驱动的。触发某些Javascript运行的每个新操作都是通过事件队列序列化的,并且在当前的Javascript流完成执行之前,不会为下一个事件提供服务。

    在node.js体系结构中,让两个Javascript独立运行并同时运行的唯一方法是为每个部分使用单独的node.js进程。然后,它们将作为两个完全独立的操作运行,操作系统将分别管理它们。如果您的计算机至少有两个核心,那么它们可以在同一时间运行,每个核心都在自己的核心上运行。如果您的计算机只有一个核心,它们将基本上位于自己的进程线程中,操作系统将对它们进行时间分片(在它们之间共享一个CPU)。

答案 1 :(得分:2)

默认情况下,执行您的JavaScript代码会在单个线程上运行。

但是,node.js尝试将大多数长时间运行的调用设为异步。对于一些只涉及执行异步OS调用的人,但对于其他一些人来说,node.js将在辅助线程上执行调用,同时继续运行其他JS代码。异步调用终止后,Js回调或Promise处理程序将运行。

答案 2 :(得分:1)

对于异步处理,Node.js 是作为实验明确创建的。与典型的基于线程的实现相比,在典型的 Web 负载下对单个线程进行异步处理可以实现更高的性能和可扩展性。