事件循环、libuv和v8引擎之间的关系

29

我正在学习Node.js的架构,以下是我的问题:

  1. 事件循环是libuv还是v8的一部分?
  2. 事件队列是事件循环的一部分吗?事件队列由libuv或v8引擎产生,还是由事件循环本身产生?
  3. libuv和v8引擎之间有什么关系?
  4. 如果事件循环是单线程的,那么libuv会创建多个线程来处理文件I/O吗?
  5. 浏览器是否具有事件循环机制,还是仅限于Node.js?

对于第二点,循环只是从队列中取出任务并执行。如果队列为空,并且没有后台线程可以潜在地向队列添加任务,则循环结束。 - justin.m.chase
4个回答

26
  1. 事件循环首先是JavaScript编程模型的重要概念。实际上,几乎所有V8嵌入式引擎都需要实现事件循环。V8提供了一个默认实现,可以由嵌入者替换或扩展。

  2. 我不理解这个问题。(我猜答案是“是”,但“事件循环”和“事件队列”的区别是什么?)

  3. 没有。(除了Node.js同时使用两者。)

  4. 是的,事件循环是单线程的。

  5. 是的,浏览器也有一个事件循环(见问题1)。


你能引用V8事件循环的参考实现作为默认实现吗?我找不到它。谢谢。 - daydreamer
1
这是“libplatform”的一部分,例如请参见PumpMessageLoophttps://cs.chromium.org/chromium/src/v8/src/libplatform/default-platform.cc?g=0&l=130 - jmrk
关于#3 - 存在明确定义的连接。节点使用V8注册函数绑定,直接调用libuv。这实际上使得V8能够访问文件系统等其他内容。 - efreezy

9
V8 项目和 libuv 项目是 NodeJS 最重要的两个依赖项之一。
在了解 NodeJS 的事件循环之前,我认为有必要先对线程有一个基本的理解。可以将线程视为需要由 CPU 执行的指令清单,CPU 将按顺序逐个执行这些线程。一个进程内部可以有多个线程。
要理解线程,也需要了解一个叫做“调度”的概念,因为 CPU 每秒只能处理有限数量的指令。
那么,V8libuvEvent Loop 之间的关系是什么呢?
如前所述,V8libuv 是 NodeJS 的依赖项,这使得在浏览器外运行 JavaScript 成为可能。每当我们在计算机上启动一个 Node 程序时,Node 自动启动一个线程并在该线程内执行一些代码。在这个单线程内部就有一个称为“Event Loop”的东西,可以将其视为控制结构,决定在任何时间点上我们的一个线程应该做什么。
它是任何 Node 程序的核心,每个 Node 程序都恰好只有一个 Event Loop
所以:

Event Loop 是 libuv 还是 V8 的一部分?

是的,Event Loop 来自由 Node 启动的进程,并具有 V8libuv 作为依赖项。

事件队列是否是事件循环的一部分?事件队列是否由 libuv 或 v8 引擎或事件循环本身生成?

如果我理解问题正确,那么事件队列更多地受到操作系统调度程序的限制,调度程序决定哪些任务最紧急并首先运行它们。

libuv 和 v8 引擎之间的联系是什么?

嗯,它们都是 NodeJS 的依赖项,都是用 C++ 编写的,V8 是 70% 的 C++,而 libuv 是 100% 的 C++。

如果事件循环是单线程的,那么 libuv 是否会创建多个线程来处理文件 I/O?

这可能令人困惑,Node 的事件循环是单线程的,但是一些 Node 标准库模块和某些框架不是单线程的。 libuv 库使 Node 具有底层访问操作系统的能力。该模块和 C++ 部分利用线程池运行计算密集型任务。
默认情况下,libuv 在线程池中创建四个线程。除了事件循环中使用的线程外,还有四个其他线程可用于卸载需要在应用程序内部进行的昂贵计算。
许多 Node 标准库中的函数都使用此线程池。所以是的,libuv 参与其中创建了一个由四个线程组成的线程池。因此,libuv 为卸载要在非常昂贵的函数调用上执行工作提供了一个线程池。 <blockquote>

浏览器是否具有事件循环机制,只有 Node.js 才有吗?

</blockquote>

我只知道 NodeJS 中存在事件循环机制。

总之,我们拥有一台双核的 2015 MacBook Pro。假设运行两个计算密集型函数,第一个函数在线程池内的第一个线程上运行,通过OS Scheduler并分配给 CPU 核心号码一。然后第二个函数被分配到池中的第二个线程,并分配到第二个 CPU 核心。因此,该线程池发生在libuv内部。
重要的一点是,如果您正在进行 http 请求,则libuv 会注意到并且 Node 没有任何代码来处理涉及网络请求的所有超低级别操作。相反,libuvhttp 请求委托给底层操作系统。
在这种情况下,libuv 用于向操作系统发出请求并等待操作系统发出的响应返回该请求。因此,由于libuv将此委托给您的操作系统,因此是您的操作系统决定是否添加新线程。这是一个例子,即一个 http 请求,在此我们不限于使用libuv的四个线程线程池。所有工作都由操作系统自己完成,我们根本没有触及线程池。

4

实际上,这并不像被选为答案的帖子所述那样简单。我希望我的评论能更加准确。希望我正确理解了Sam Roberts(IBM)在有关Node事件循环的讲话中的观点。

您可以在此处查看演讲:https://www.youtube.com/watch?v=P9csgxBgaZ8

这是对@jmrk给出的答案的补充。

Libuv将任务委托给底层操作系统。然后,操作系统负责在您监听的事件发生时发送通知。它会执行Node中许多操作,例如:套接字(net/dgram/http/tls/https/child_process pipes、stdin、out、err)、超时和间隔等。

但是,并非所有事情都可以像这样委托给底层操作系统。有时需要创建线程(默认情况下有4个线程,但您可以使用UV_THREADPOOL_SIZE更改此设置)。不可轮询的操作包括文件系统操作、dns.lookup()和某些加密函数。


0

请仔细阅读以下内容:

  1. V8引擎是火车教练的引擎。它有一定的职责,包括提供事件循环以运行异步任务。

  2. 事件循环是执行异步任务的核心。当C++ Web API完成一个函数(任务)时,回调被调用。它被移动到事件队列中,并等待堆栈变为空。因此,事件队列是事件循环的一部分,并由事件循环生成。

  3. V8引擎用于执行我们编写的JavaScript代码,而libuv是一个库,用于在Node.js中提供多线程功能以执行长时间运行的进程。

  4. 事件循环是单线程的,但Node.js不是单线程的,因为它在其运行时具有libuv线程池,负责多线程。

  5. 浏览器API也提供事件循环。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接