一尘不染

什么时候使用线程池?

node.js

因此,我对Node.js的工作方式有所了解:它具有一个侦听器线程,该线程接收事件,然后将其委托给工作池。工作线程一旦完成工作,便会通知侦听器,然后侦听器将响应返回给调用者。

我的问题是:如果我在Node.js中建立一个HTTP服务器,并在我的一个路由路径事件(例如“ / test /
sleep”)中调用sleep,那么整个系统就会停顿下来。甚至是单个侦听器线程。但是我的理解是这段代码正在工作池中发生。

现在,相比之下,当我使用Mongoose与MongoDB交谈时,数据库读取是一项昂贵的I /
O操作。Node似乎能够将工作委托给线程并在完成时接收回调。从数据库加载所需的时间似乎不会阻塞系统。

Node.js如何决定使用线程池线程还是侦听器线程?为什么我不能编写仅休眠并阻塞线程池线程的事件代码?


阅读 339

收藏
2020-07-07

共1个答案

一尘不染

您对节点如何工作的理解是不正确的……但这是一个普遍的误解,因为这种情况的实际情况实际上相当复杂,并且通常归结为诸如“ node is single
threaded”之类的小词,过分简化了事情。

目前,我们将忽略通过集群webworker-
threads进行的
显式多处理/多线程,而仅讨论典型的非线程节点。

节点在单个事件循环中运行。它是单线程的,您只能获得一个线程。您编写的所有JavaScript都会在此循环中执行,并且如果该代码中发生了阻塞操作,则它将阻塞整个循环,直到完成为止,其他任何事情都不会发生。这是您经常听到的节点的典型单线程性质。但是,这还不是全部。

通常使用C / C ++编写的某些功能和模块支持异步I /
O。当您调用这些函数和方法时,它们在内部管理将调用传递给工作线程。例如,当您使用fs模块请求文件时,fs模块将该调用传递给工作线程,而该工作线程等待其响应,然后将其呈现回事件循环,该循环在没有它的情况下一直在进行与此同时。所有这些都是从您(节点开发人员)那里抽象出来的,其中一些是通过使用libuv从模块开发者那里抽象出来的。

正如Denis
Dollfus在评论中指出的(,libuv用于实现异步I /
O的策略并不总是线程池,特别是在http模块的情况下,另一种策略似乎是目前使用。就我们的目的而言,最重要的是要注意如何实现异步上下文(通过使用libuv),并且libuv维护的线程池是该库提供的实现异步的多种策略之一。


在一篇非常相关的切线上,这篇出色的文章对节点如何实现异步性以及一些相关的潜在问题以及如何处理这些问题进行了更深入的分析。它的大部分内容是在我上面写的内容基础上扩展的,但另外指出:

  • 您包含在项目中的任何使用本机C ++和libuv的外部模块都可能使用线程池(请考虑:数据库访问)
  • libuv的默认线程池大小为4,并使用队列来管理对线程池的访问-结果是,如果您有5个长时间运行的数据库查询全部同时进行,则其中之一(以及任何其他异步查询)依赖于线程池的操作)将等待查询结束,甚至无法开始
  • 您可以通过UV_THREADPOOL_SIZE环境变量增加线程池的大小来缓解这种情况,只要您在需要并创建线程池之前就这样做即可:process.env.UV_THREADPOOL_SIZE = 10;

如果您希望在节点中进行传统的多处理或多线程处理,则可以通过内置cluster模块或上述其他各种模块来获取webworker- threads,也可以通过实现某种方式将工作分块并手动使用setTimeout或进行伪造。setImmediateprocess.nextTick暂停您的工作并在以后的循环中继续进行,以完成其他过程(但不建议这样做)。

请注意,如果您使用javascript编写长时间运行/阻止的代码,则可能是在犯错误。其他语言将更有效地执行。

2020-07-07