Javascript 不应该';Node.js中的异步编程是否会导致堆栈溢出?
我刚刚意识到(单线程)Node.js有一个问题:Javascript 不应该';Node.js中的异步编程是否会导致堆栈溢出?,javascript,node.js,Javascript,Node.js,我刚刚意识到(单线程)Node.js有一个问题: 服务器开始响应请求,请求一直运行,直到由于I/O阻塞为止 当请求处理器阻塞时,服务器启动并返回到步骤1,处理更多请求 每当请求处理器阻塞I/O时,服务器都会检查任何请求是否已完成。它以FIFO的方式处理这些数据以响应客户机,然后像以前一样继续处理 这难道不意味着如果太多的请求开始相互阻塞,而没有一个请求完成,那么#2处应该会出现堆栈溢出吗?为什么/为什么不?node.js基于谷歌的V8 JavaScript引擎,该引擎使用事件循环 看 n
这难道不意味着如果太多的请求开始相互阻塞,而没有一个请求完成,那么#2处应该会出现堆栈溢出吗?为什么/为什么不?node.js基于谷歌的V8 JavaScript引擎,该引擎使用事件循环 看
node.js
通过在任何地方使用异步技术来防止您描述的堆栈过度增长1
任何可能阻塞的操作都会使用回调进行进一步处理,而不是阻塞调用。这完全避免了堆栈增长,并使重新进入事件循环变得容易(它“驱动”了底层的真实I/O和请求调度)
考虑以下伪代码:
fun() {
string = net.read();
processing(string);
}
线程在读取时被阻塞,堆栈只能在两次读取都完成并且处理完成后释放
现在,如果您的所有代码如下所示:
fun() {
net.read(onDone: processing(read_data));
}
如果您实现了,请阅读以下内容:
net.read(callback) {
iorequest = { read, callback };
io.push_back(iorequest);
}
fun
只要read
可以将读I/O与相关回调一起排队,就会完成fun
的堆栈在没有阻塞的情况下被重绕-它“立即”返回到事件循环,没有任何线程堆栈剩余
也就是说,您可以转到下一个回调(重新进入事件循环),而无需在线程堆栈上保留任何每个请求的数据
因此node.js
通过在“用户”代码中发生阻塞调用的地方使用异步回调来避免堆栈过度增长
有关这方面的更多信息,请查看node.js
页面以及最后链接的第一组幻灯片
我想差不多吧
你在评论中提到。使用这种类型的处理,队列中的APC被允许阻塞,队列中的下一个APC在线程堆栈上得到处理,使其成为“递归”调度
假设我们有三个APC待处理(A
、B
和C
)。我们得到:
初始状态:
Queue ABC
Stack xxxxxxxx
线程休眠,因此APC调度开始,进入处理:
Queue BC
Stack AAAAxxxxxxxx
A块,B在同一堆栈上调度:
B块,C被调度:
Queue
Stack CCCCCCCBBBBBBAAAAxxxxxxxx
很明显,如果有足够多的阻塞APC挂起,堆栈最终会爆炸
使用node.js
,不允许阻止请求。相反,这里是一个模型,模拟了同样的三个请求会发生什么:
Queue ABC
Stack xxxxxxxx
A开始处理:
Queue BC
Stack AAAAxxxxxxxx
现在A需要做一些阻塞的事情——在node.js
中,它实际上不能。它所做的是排队等待另一个请求(A'
)(可能是一个上下文-简单地说是一个包含所有变量的哈希):
然后A返回并返回到:
I/O queue A'
Queue BC
Stack xxxxxxxx
注意:不再使用堆叠框架。I/O挂起队列实际上是由操作系统管理的(使用epoll
或kqueue
或其他方法)。主线程检查事件循环中的OS I/O就绪状态和挂起(需要CPU)队列
然后B获得一些CPU:
I/O queue A'
Queue C
Stack BBBBBBBxxxxxxxx
同样,B想要进行I/O。它将一个新的回调排入队列,返回
I/O queue A'B'
Queue C
Stack xxxxxxxx
如果B的I/O请求同时完成,则下一个快照可能如下所示
I/O queue A'
Queue B'
Stack CCCCCxxxxxxxx
在处理线程上,任何时候都不存在多个回调堆栈帧。API不提供阻塞调用,堆栈不会表现出APC模式所表现出的递归增长类型。node.js
通过在任何地方使用异步技术来防止所描述的堆栈过度增长1
任何可能阻塞的操作都会使用回调进行进一步处理,而不是阻塞调用。这完全避免了堆栈增长,并使重新进入事件循环变得容易(它“驱动”了底层的真实I/O和请求调度)
考虑以下伪代码:
fun() {
string = net.read();
processing(string);
}
线程在读取时被阻塞,堆栈只能在两次读取都完成并且处理完成后释放
现在,如果您的所有代码如下所示:
fun() {
net.read(onDone: processing(read_data));
}
如果您实现了,请阅读以下内容:
net.read(callback) {
iorequest = { read, callback };
io.push_back(iorequest);
}
fun
只要read
可以将读I/O与相关回调一起排队,就会完成fun
的堆栈在没有阻塞的情况下被重绕-它“立即”返回到事件循环,没有任何线程堆栈剩余
也就是说,您可以转到下一个回调(重新进入事件循环),而无需在线程堆栈上保留任何每个请求的数据
因此node.js
通过在“用户”代码中发生阻塞调用的地方使用异步回调来避免堆栈过度增长
有关这方面的更多信息,请查看node.js
页面以及最后链接的第一组幻灯片
我想差不多吧
你在评论中提到。使用这种类型的处理,队列中的APC被允许阻塞,队列中的下一个APC在线程堆栈上得到处理,使其成为“递归”调度
假设我们有三个APC待处理(A
、B
和C
)。我们得到:
初始状态:
Queue ABC
Stack xxxxxxxx
线程休眠,因此APC调度开始,进入处理:
Queue BC
Stack AAAAxxxxxxxx
A块,B在同一堆栈上调度:
B块,C被调度:
Queue
Stack CCCCCCCBBBBBBAAAAxxxxxxxx
很明显,如果有足够多的阻塞APC挂起,堆栈最终会爆炸
使用node.js
,请求不会被分配