nodejs 通过将 I/O 委托给内核来隐式生成线程。这与每个请求创建一个线程的服务器有何不同
nodejs spawns threads implicitly by delegating the I/O to the kernel. How is this different than a server that makes a thread per request
跟进我之前提出的两个问题的想法:
最近在研究nodejs。它被宣传为 "single threaded",这在一定程度上是正确的,因为您的所有 JS 在一个线程上都执行 运行,但是根据我的阅读,在后台,节点通过委托 I/O 来实现这一点任务分配给内核,这样它就不会因为等待响应而卡住。
我很难理解的是,这与您明确为每个请求创建一个线程的范例有何不同。
有人可以解释深度差异吗?
如果节点为每个 I/O 请求创建一个线程,这就是正确的。但是,当然,它不会那样做。它有一个 I/O 引擎,可以理解在每个平台上执行 I/O 的最佳方式。
nodejs 对你隐藏的不是一些天真的实现,调度实体等待每个请求完成,而是一个复杂的实现,它理解在每个平台上执行 I/O 的最佳方式.
更新:
If both approaches need the kernel for I/O aren't they both creating a kernel thread per request?
没有。 I/O 有很多使用内核的方法,不需要每个请求都有一个内核线程。它们因平台而异。 Windows 有 IOCP。 Linux 有 epoll。等等。
If nodejs somehow is using a fixed amount of threads and queueing the I/O operations, isn't that slower than a thread per request?
不,由于各种原因,它通常要快得多,具体取决于每个平台的具体情况。这里有一些优点:
- 当大量 I/O 一次完成时,您可以避免 "thundering herds"。相反,您可以只唤醒可以同时有用 运行 的线程数。
- 您可以避免需要大量的上下文切换来让所有不同的线程执行。相反,每个线程都可以在完成后处理完成。
- 您不必为每个 I/O 操作将每个线程都放在等待队列中。相反,您可以为线程组使用单个等待队列。
为了让您了解它的重要性,请考虑每个 I/O 使用一个线程和在 Linux 上使用 epoll 之间的区别。如果每个 I/O 使用一个线程,这意味着每个 I/O 操作都需要一个线程将自己置于等待队列中,该线程被阻塞,该线程被解除阻塞,发生上下文切换线程,然后该线程将其自身从等待队列中移除。
相比之下,使用 epoll,单个线程可以为任意数量的 I/O 完成提供服务,而不必为每个 I/O 重新安排或添加到等待队列或从等待队列中删除。同样,一个线程可以发出多个 I/O 请求而不会被取消调度。这种差异是巨大的。
跟进我之前提出的两个问题的想法:
最近在研究nodejs。它被宣传为 "single threaded",这在一定程度上是正确的,因为您的所有 JS 在一个线程上都执行 运行,但是根据我的阅读,在后台,节点通过委托 I/O 来实现这一点任务分配给内核,这样它就不会因为等待响应而卡住。
我很难理解的是,这与您明确为每个请求创建一个线程的范例有何不同。
有人可以解释深度差异吗?
如果节点为每个 I/O 请求创建一个线程,这就是正确的。但是,当然,它不会那样做。它有一个 I/O 引擎,可以理解在每个平台上执行 I/O 的最佳方式。
nodejs 对你隐藏的不是一些天真的实现,调度实体等待每个请求完成,而是一个复杂的实现,它理解在每个平台上执行 I/O 的最佳方式.
更新:
If both approaches need the kernel for I/O aren't they both creating a kernel thread per request?
没有。 I/O 有很多使用内核的方法,不需要每个请求都有一个内核线程。它们因平台而异。 Windows 有 IOCP。 Linux 有 epoll。等等。
If nodejs somehow is using a fixed amount of threads and queueing the I/O operations, isn't that slower than a thread per request?
不,由于各种原因,它通常要快得多,具体取决于每个平台的具体情况。这里有一些优点:
- 当大量 I/O 一次完成时,您可以避免 "thundering herds"。相反,您可以只唤醒可以同时有用 运行 的线程数。
- 您可以避免需要大量的上下文切换来让所有不同的线程执行。相反,每个线程都可以在完成后处理完成。
- 您不必为每个 I/O 操作将每个线程都放在等待队列中。相反,您可以为线程组使用单个等待队列。
为了让您了解它的重要性,请考虑每个 I/O 使用一个线程和在 Linux 上使用 epoll 之间的区别。如果每个 I/O 使用一个线程,这意味着每个 I/O 操作都需要一个线程将自己置于等待队列中,该线程被阻塞,该线程被解除阻塞,发生上下文切换线程,然后该线程将其自身从等待队列中移除。
相比之下,使用 epoll,单个线程可以为任意数量的 I/O 完成提供服务,而不必为每个 I/O 重新安排或添加到等待队列或从等待队列中删除。同样,一个线程可以发出多个 I/O 请求而不会被取消调度。这种差异是巨大的。