Node.JS中的单线程事件循环与multithreading非阻塞工作程序

Node.JS最大的优点是它的非阻塞性质。 它是单线程的,所以它不需要为每个新的传入连接产生一个新的线程。

事件循环(事实上是单线程)的后面,有一个“非阻塞工作者”。 这个东西不再是单线程的,所以(就我所知),它可以为每个任务产生一个新的线程。

也许我误解了一些东西,但究竟是什么优势。 如果需要处理很多任务,那么不会将非阻塞工作变成阻塞工作者吗?

感谢基督徒

您需要阅读有关libuv的信息 ,即节点非阻塞I / O背后的“魔力”。

libuv书中重要的一点是libuv使用主机操作系统的asynchronous I / O 设备 , 它不会简单地为每个连接创build一个新的线程

libuv告诉操作系统,它想知道在一组特定套接字上发生的任何变化(连接状态,收到的数据等)。 然后由操作系统来pipe理连接。 操作系统本身可能会创build一个或多个线程来完成,但这不是我们所关心的。 (它肯定不会为每个连接创build一个线程。)

对于其他types的操作,如调用可能在计算上很昂贵的C库(即encryption),libuv提供了一个可以运行这些操作的线程池 。 由于它是一个线程池,所以您不必担心线程数量增长不受限制。 当池完全忙时,操作排队。

所以是的,JavaScript运行在一个单一的线程。 是的,节点(通过libuv)在后台产生许multithreading来工作,以便它不需要阻塞JavaScript线程。 然而,总是控制线程数,I / O通常甚至没有得到它自己的节点分配线程,因为这是由OS处理的。

好吧,让我们分解一下。 单线程应用程序具有以下优点:永远不会遇到死锁或竞态条件。 这些问题源于multithreading系统中的同时内存访问。 如果两个线程访问相同的信息可能会发生奇怪的事情。

那么为什么JavaScript有工人? 如果你需要做一些繁重的处理,你会阻止事件循环,你可以尝试通过生成计时器事件来分解工作负载,但是这很枯燥。 Worker允许你在一个条件下产生一个线程: 没有共享内存访问 。 这解决了在单线程环境中繁重处理的问题,同时避免了multithreading环境(死锁,竞态条件)的缺陷。

而@dandavis说,如果你有一个多核心的CPU(现在每个人都这么做),工作者线程可以被卸载到其他核心。

你必须明白,虽然JavaScript是单线程的,但它周围的环境仍然是非常multithreading的。 读取文件在Node.JS中是非阻塞的,但在OS中可能有一个线程支持它。


作为一个小附录,我会说Node.JS最大的优点是它允许你在服务器上编写JavaScript,它允许你在客户端和服务器之间共享代码 。 非阻塞的事实很好,但是线程已经解决了这个问题。 非阻塞IO来源于单线程。 有一个阻塞IO的单线程是非常不方便的。

Node.js不需要旋转一个新的线程来处理每个Web请求,这一事实在您拥有大量并发连接的环境中(如在成千上万个并发连接中)是一个优势。可能不会看到像Java / Apache或C#/ IIS这样的线程环境有任何显着的区别,因为这些平台现在已经很好的优化了。

其优点是,即使Node.js为I / O操作(在某些情况下可能会但不在其他情况下)旋转一个新线程,您仍然可以减less处理Web请求所需的线程数量,从1 -request to 1.同样,如果你正在处理成千上万的并发连接,这是一个巨大的收益。