【问题标题】:How Nodejs's internal threadpool works exactly?Nodejs 的内部线程池是如何工作的?
【发布时间】:2015-06-06 22:13:26
【问题描述】:

我已经阅读了很多关于 NodeJs 如何工作的文章。但是我仍然无法弄清楚Nodejs的内部线程是如何进行IO操作的。

在这个答案https://stackoverflow.com/a/20346545/1813428 中,他说NodeJs的线程池中有4个内部线程来处理I/O操作。那么如果我有 1000 个请求同时到来,每个请求都想做 I/O 操作,比如从数据库中检索大量数据。 NodeJs 会将这些请求分别传递给这 4 个工作线程,而不会阻塞主线程。所以 NodeJs 可以同时处理的最大 I/O 操作数是 4 个操作。我错了吗?

如果我是对的,剩下的请求将在哪里处理?主单线程是非阻塞的,不断地向对应的算子发起请求,那么在所有的worker线程都满载任务的情况下,这些请求会去哪里呢? .

在下图中,所有的内部工作线程都充满了任务,假设它们都需要从数据库中检索大量数据,并且主单线程不断驱动新的请求这些工人,这些请求会去哪里?它是否有一个内部任务队列来存储这些请求?

【问题讨论】:

  • 您对 C 或 C++ 的熟悉程度如何?
  • 这 1000 个请求来自单个以太网/wifi 卡。硬件本身只能处理字节流。没有 1000 个并行网络请求之类的东西,除非您的机器上有多个连接到多个调制解调器的以太网卡连接到您的 ISP(通常这意味着多个 Internet 帐户)。现实世界中实际存在的是 1000 个网络请求轮流在单个连接上发送位 - 这样做速度足够快,您会产生 1000 个并行请求的错觉..
  • ... 因为是这种情况(在现实世界中通常没有并行请求之类的东西)node.js 避免了大多数人使用的多线程的内存和上下文切换开销(大多数人,不是所有人 - 其他语言也开始变得聪明,例如 Java 的 Netty 在单个线程上完成所有网络处理)。 Node.js 并行等待,而不是尝试并行执行代码。等待恰好需要 0% 的 CPU 时间。

标签: javascript node.js multithreading event-loop


【解决方案1】:

在下图中,所有内部工作线程都充满了任务,假设它们都需要从数据库中检索大量数据,并且主单线程不断向这些工作人员发送新请求

这不是 node.js 使用这些线程的方式。

根据Node.js documentation,线程是这样使用的:

所有请求和响应都在主线程中“处理”。 您的回调(以及await 之后的代码)只是轮流执行。 javascript 解释器和“事件循环”之间的“循环”通常只是一个 while 循环。

除了你自己启动的 worker_threads 之外,node.js 只有 4 件事使用线程:等待 DNS 响应、磁盘 I/O、内置的 crypto 库和内置的 zip 库。 Worker_threads 是 node.js 在主线程之外执行 javascript 的唯一地方。线程的所有其他用途都执行 C/C++ 代码。

如果你想了解更多,我已经写了几个相关问题的答案:

Node js architecture and performance

how node.js server is better than thread based server

node js - what happens to incoming events during callback excution

Does javascript process using an elastic racetrack algorithm

Is there any other way to implement a "listening" function without an infinite while loop?

【讨论】:

  • 你是否厌倦了在分叉集群实例下使用多个worker_thread?如果加密使用更多的工作线程,那么我想它会工作得很好?
【解决方案2】:

libuv 提供的单个每个进程的线程池默认创建 4 个线程。 UV_THREADPOOL_SIZE 环境变量可用于更改node 进程启动时创建的线程数,最大值为 1024(截至 libuv 版本 1.30.0)。

当所有这些线程都被阻塞时,使用它们的进一步请求将排队。请求线程的 API 方法称为uv_queue_work

此线程池用于任何会导致阻塞 IO 的系统调用,其中包括本地文件系统操作。正如@Andrey 所提到的,它还可用于减少 CPU 密集型操作的影响。

非阻塞 IO,大多数网络操作都支持,不需要使用线程池。

如果您正在使用的数据库驱动程序的源代码可用并且您能够找到对uv_queue_work 的引用,那么它可能正在使用线程池。

如果需要,libuv thread pool 文档会提供更多技术细节。

【讨论】:

    【解决方案3】:

    不,线程池的主要用例是卸载 CPU 密集型操作。 IO 在一个线程中执行 - 如果您正在并行等待外部数据,则不需要多个线程,而事件循环正是一种组织执行流程的技术,以便您尽可能多地并行等待

    示例: 您需要发送 100 封带有问题 (y/n) 的电子邮件和另一封带有回答“y”的电子邮件。写电子邮件大约需要 30 秒,回复平均需要两个小时 + 阅读回复需要 10 秒。您首先编写所有 100 封电子邮件(50 分钟的时间),然后等待每次回复到达时都会唤醒您的警报声,当您收到回复时,您会增加“y”的数量。在约 2 小时 50 分钟内完成。这是异步 IO 和事件循环的示例(无线程池)

    阻止示例:发送电子邮件、等待答复、重复。需要 4 天(如果您可以克隆另一个,则需要两天)

    异步线程池示例:每个响应都使用您不知道的语言。你有 4 个翻译朋友。您通过电子邮件将文本发送给他们,他们将翻译后的文本通过电子邮件发送给您(或者,更准确地说:您打印文本并将其放入“需要翻译”文件夹。只要有翻译器,就会从文件夹中提取文本)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-06-26
      • 1970-01-01
      • 2014-01-19
      • 2014-05-16
      • 2018-01-20
      • 1970-01-01
      • 1970-01-01
      • 2014-09-27
      相关资源
      最近更新 更多