【问题标题】:Is there any other way to implement a "listening" function without an infinite while loop?有没有其他方法可以在没有无限while循环的情况下实现“监听”功能?
【发布时间】:2020-07-30 09:21:23
【问题描述】:

我一直在思考诸如 React 之类的代码和库,它们会自动对发生的事件做出反应,并且想知道所有这些是如何在较低级别的 C++ 和机器代码中实现的。

我似乎想不出任何其他方式,比如事件侦听器可以用 if not with a while loop running on another thread。

所以这一切都在幕后吗?只是 while 一直循环下去?例如,RethinkDB 将自己宣传为具有repubsub 库的“实时数据库”。 “订阅”方法是否只是在后台使用 while 循环实现的?我似乎找不到任何相关信息。

比如,插座和其他东西。当计算机在端口上“侦听”套接字连接时,该计算机是否正在运行类似的东西:

while(1) {
    if (connectionFound) {
        return True;
    }
}

或者我缺少什么?

【问题讨论】:

  • 当事件发生时自动对事件做出反应 它使用addEventListener 来监听事件。不需要循环。
  • 基本上使用这样的循环可能是解决问题的最糟糕的方法。 任何事情都比这更好。值得庆幸的是,整个 JavaScript 生态系统都基于异步行为和事件处理机制。
  • 在 JavaScript 运行时的下一层,有一个基于电子信号的操作系统,因此操作系统的核心可以响应来自 I/O 硬件的中断。在此基础上,您将获得 JavaScript 支持的异步事件模型
  • @CertainPerformance 但addEventListener 是如何实现的?
  • 如果您有兴趣对经典 JS 事件以外的事物做出反应,可以使用一些浏览器实现的 API:developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverdeveloper.mozilla.org/en-US/docs/Web/API/MutationObserver

标签: javascript reactjs loops events rethinkdb


【解决方案1】:

我已将这个问题的答案写在另一个答案中。通常我会以duplicate and point to that answer 结束这个问题,但这是一个非常不同的问题。另一个问题是关于 javascript 性能的。为了回答这个问题,我必须先写下这个问题的答案。

因此,我将做一些通常不应该做的事情:我将复制我对另一个问题的部分答案。所以这是我的答案:

javascript 和 node.js 等待的实际事件根本不需要循环。事实上,它们需要 0% 的 CPU 时间。

异步 I/O 的工作原理(在任何编程语言中)

硬件

如果我们真的需要了解节点(或浏览器)内部的工作原理,不幸的是,我们必须首先了解计算机的工作原理——从硬件到操作系统。是的,这将是一次深入的潜水,所以请耐心等待..

这一切都始于中断的发明..

这是一个伟大的发明,也是一个潘多拉魔盒 - Edsger Dijkstra

是的,上面的引用来自同一个“Goto 被认为有害”Dijkstra。从一开始,将异步操作引入计算机硬件就被认为是一个非常困难的话题,即使对于业内的一些传奇人物来说也是如此。

引入中断是为了加快 I/O 操作。硬件无需在无限循环中使用软件轮询某些输入(占用 CPU 时间从有用的工作中),而是向 CPU 发送信号以告知它发生了事件。然后 CPU 将暂停当前正在运行的程序并执行另一个程序来处理中断 - 因此我们将这些函数称为中断处理程序。而“handler”这个词一直卡在堆栈中,直到GUI库调用回调函数“事件处理程序”。

如果您不熟悉中断并想了解更多信息,维基百科实际上有一篇相当不错的文章:https://en.wikipedia.org/wiki/Interrupt

如果您一直关注,您会注意到中断处理程序的这个概念实际上是一个回调。您将 CPU 配置为在稍后发生事件时调用函数。所以即使是回调也不是一个新概念——它比 C 更古老。

操作系统

中断使现代操作系统成为可能。如果没有中断,CPU 将无法暂时停止您的程序以运行操作系统(好吧,有协作多任务,但现在让我们忽略它)。操作系统的工作原理是它在 CPU 中设置一个硬件定时器来触发中断,然后它告诉 CPU 执行你的程序。运行您的操作系统的正是这种周期性定时器中断。

除了定时器,操作系统(或者更确切地说是设备驱动程序)为 I/O 设置中断。当 I/O 事件发生时,操作系统将接管您的 CPU(或多核系统中的一个 CPU)并检查其数据结构,它需要执行下一个处理 I/O 的进程(这称为抢占式多任务处理)。

从键盘和鼠标到存储到网卡的所有内容都使用中断来告诉系统有数据要读取。如果没有这些中断,监控所有这些输入将占用大量 CPU 资源。中断非常重要,以至于它们通常被设计到 USB 和 PCI 等 I/O 标准中。

进程

现在我们已经清楚地了解了这一点,我们可以理解 node/javascript 是如何实际处理 I/O 和事件的。

对于 I/O,各种操作系统都有各种不同的 API 来提供异步 I/O - 从 Windows 上的重叠 I/O 到 Linux 上的 poll/epoll 到 BSD 上的 kqueue 到跨平台的 select()。 Node 在内部使用 libuv 作为对这些 API 的高级抽象。

这些 API 的工作原理相似,但细节不同。本质上,它们提供了一个函数,当被调用时会阻塞你的线程,直到操作系统向它发送事件。所以是的,即使是非阻塞 I/O 也会阻塞你的线程。这里的关键是阻塞 I/O 会在多个地方阻塞你的线程,但非阻塞 I/O 只会在一个地方阻塞你的线程——你等待事件的地方。

查看我对其他问题的回答,了解有关此类 API 在 C/C++ 级别如何工作的更具体示例:I know that callback function runs asynchronously, but why?

对于按钮单击和鼠标移动等 GUI 事件,操作系统只需跟踪您的鼠标和键盘中断,然后将它们转换为 UI 事件。这让您无需知道按钮、窗口、图标等位置的软件窗体就可以解放出来。

这允许您以面向事件的方式设计您的程序。这类似于中断允许操作系统设计人员实现多任务处理的方式。实际上,异步 I/O 之于框架就像中断之于操作系统一样。它允许 javascript 花费 0% 的 CPU 时间来处理(等待)I/O。这就是使异步代码速度更快的原因 - 它并不是真的更快,但不会浪费时间等待。

这个答案相当长,所以我会留下指向我对与该主题相关的其他问题的答案的链接:

Node js architecture and performance(注意:这个答案提供了一些关于事件和线程关系的见解 - tldr:操作系统在内核事件之上实现线程)

Does javascript process using an elastic racetrack algorithm

how node.js server is better than thread based server

【讨论】:

  • 太棒了,非常感谢!喜欢“深潜”,很多人不这样做。
  • 你是谁!总是令人着迷的答案。 +1
【解决方案2】:

“听众”和“订阅”只是想法。一切都可以用 lambdas 抽象出来。这是一种可能的实现方式-

const logger =
  // create a new "listener",
  // send any data we "hear" to console.log
  listen(console.log)

// implement so-called "listener"
const listen = (responder) =>
  x => (responder(x), x)

// run it synchronously
logger(1)
logger(2)

// or asynchronously
setTimeout(_ => logger(3), 2000)

// 1
// 2
// some time later...
// 3

假设我们有一个

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-30
    • 2013-11-07
    • 1970-01-01
    • 2018-07-29
    • 1970-01-01
    • 1970-01-01
    • 2019-08-23
    • 2015-05-21
    相关资源
    最近更新 更多