NodeJS异步I / O执行

时间:2015-05-13 02:43:20

标签: javascript node.js multithreading asynchronous

据我了解,虽然显然有一个帮手'线程,Node.js在单个线程中运行,因此,事件循环堆栈中的每个操作一个接一个地运行并且其他操作排队,而Node在后台执行异步I / O,这样服务器就能够执行其他操作在进行非阻塞I / O而不需要创建浪费的多线程的同时,I / O完成并且它的相关回调被拉入事件循环队列,这是关于节点

但是,在我已经准备好的许多文章中,不清楚异步I / O操作是否与I / O中的其他I / O操作并行运行单独的线程或进程,或者是否每个请求的I / O操作在事件循环执行其他操作时,在辅助线程中一个接一个地运行。在阅读了短语"除了您的代码之外,其他所有内容并行运行"这让我更加困惑。

问题是,多线程还是非多线程?如果每个异步操作都在一个单独的线程中运行,那么它是否使用了与Apache服务器一样多的资源?

2 个答案:

答案 0 :(得分:3)

Node本质上是非多线程的。异步性比Node更深入,比libuv更深,甚至比libuv使用的设施(epollkqueueIOCP等)更深。

当内核获得异步请求时,它不会启动另一个执行线程。相反,它会将其添加到一个简单的“要注意的事项”列表中。"例如,如果进程发出网络读取请求,则内核将在该列表上创建一个条目。它就像是"嘿,下次有一个看起来像这样的读取请求时,让流程知道它。"在进行此输入后,内核将控制权返回给流程,并且两者都以快乐的方式进行。唯一幸存的是列表中的数据。

通过hardware interrupts.内核被告知网络读取事件使用中断,处理器将内核拉入一个特殊的循环 - 阻止它此刻正在做的任何事情 - 并告诉它事件。然后内核检查其未完成的请求列表,并且(在kevent AIO情况下)向进程发送类似的中断(以信号的形式),让它知道网络读取。所以,没有线程。只是中断。

嗯,这有点简化:在非AIO kevent和epoll案例中,在内核获得网络读取之后,它只是将它放在事件列表中。该过程会定期检查该事件列表,以查看是否有其他内容。

此外,从内核视图来看,这就是所有I / O的工作方式。最大的区别在于内核并不需要进程等待内核返回它。

libuv中实际上有a little more complexity作为非网络请求(以及DNS请求,这是特殊的,痛苦的网络请求形式)由线程处理。这是因为用于制作异步的内核工具通常不是很好,如果它们存在的话。

答案 1 :(得分:1)

它不是多线程的,只有一个小例外。我们稍后再谈谈这个例外。首先,让我们看看为什么事情可以并行发生但不是多线程的。

等待I / O

当您通过网络发送内容时,例如,发出http请求,等待http响应完成或等待mysql响应您的软件没有做任何事情。

那么,如果您的软件没有做任何事情,网络如何运作? (对不起,如果这很明显,但指出显而易见的通常是显而易见的事情)

有些事情发生在CPU外面

首先,大多数网络都在CPU之外。你有你的网卡缓冲数据输出和进来。你有网络电缆中的电子或空间/空气中的光子根据网络设备发送的信号振动。你有你的路由器,你的ISP,地球另一边的服务器等。

以上所有内容都需要处理,以便您的http请求返回数据。在大多数语言中,虽然发生了上述所有情况,但您的代码将无法执行任何操作。

在javascript中不是这样。当发出I / O请求时,不是等待数据返回,解释器只会注册一个回调,当数据最终到达时,你执行该回调。既然已经完成了,其他代码就可以执行了。也许之前请求的其他一些数据现在在这里,并且可以执行回调。也许setTimeout已经过期,是时候调用该回调了。

所以多个事情可以并行发生,大部分都在你的过程之外,很多都在你的CPU之外,其中一些在另一台机器上,甚至可能在地球的另一边。在这种情况下,javascript允许您运行一些代码。

例外:磁盘I / O

例外是磁盘I / O.在最低级别(实际上,从次到最低)C仅为I / O公开fread()fwrite()等同步函数。读取网络数据包在技术上也是同步的。不同之处在于网络没有立即响应,因此网络代码有很多时间来等待数据包。在这些读取和写入之间,javascript运行您的代码。但文件系统很乐意告诉您数据立即可用。因此,与网络代码不同,从磁盘读取的代码将花费大部分时间来忙碌。

有几种解决方法。有些操作系统甚至有异步API来从磁盘读取。 node.js开发人员决定通过生成另一个线程来执行磁盘I / O来处理这种情况。所以对于磁盘I / O来说它是并行的,因为它是多线程的。