要了解这里发生的情况,您必须了解有关节点事件循环以及操作系统和 CPU 的一些知识。
首先,让我们更好地理解这段代码。你称这为递归,但真的是这样吗?
在递归中,我们通常会想到嵌套的调用堆栈,然后当计算完成(达到基本情况)时,堆栈“展开”回到调用递归函数的位置。
虽然这是一个调用自身的方法(通过回调间接调用),但事件循环会扭曲实际发生的情况。
process.nextTick 将一个函数作为回调,并将其放在事件循环的下一次循环中要完成的任务列表中的第一个位置。然后执行该回调,完成后,您再次注册相同的回调。从本质上讲,这种递归与真正的递归之间的主要区别在于,我们的调用堆栈永远不会超过一次调用深度。我们从不“展开”筹码堆,我们只是连续拥有许多小的短筹码堆。
好吧,那为什么这很重要呢?
当我们更好地理解事件循环以及到底发生了什么时,我们可以更好地理解系统资源是如何使用的。通过以这种方式使用 process.nextTick,您可以确保事件循环中始终有一些事情要做,这就是为什么您会获得高 CPU 使用率(但您已经知道了)。现在,如果我们假设您的 HTTP 服务器与脚本在同一进程中运行,如下所示
function interval(){
process.nextTick(doIntervalStuff)
}
function doIntervalStuff() {
someSmallSyncCode();
interval();
}
http.createServer(function (req, res) {
doHTTPStuff()
}).listen(1337, "127.0.0.1");
那么 CPU 使用率如何在程序的两个不同部分之间分配呢?好吧,这很难说,但如果我们理解事件循环,我们至少可以猜测。
由于我们使用 process.nextTick,因此 doIntervalStuff 函数将在每次事件循环“开始”时运行,但是,如果需要为 HTTP 服务器执行某些操作(例如处理连接),那么我们知道将会得到在下一次事件循环开始之前完成,并记住,由于节点的事件性质,这可能会在事件循环的一次迭代中处理任意数量的连接。这意味着,至少在理论上,进程中的每个函数都会获得它“需要”的 CPU 使用率,然后 process.nextTick 函数使用其余的。虽然这并不完全正确(例如,您的一些阻塞代码会将其搞砸),但它是一个足够好的模型值得考虑。
好吧,现在(最后)谈谈你真正的问题,单独的流程怎么样?
有趣的是,操作系统和 CPU 本质上通常也非常“事件”。每当一个进程想要做某事时(例如在节点的情况下,启动事件循环的迭代),它会向操作系统发出要处理的请求,然后操作系统将该作业推入就绪队列(按优先级排列)当 CPU 调度程序决定抽出时间来处理它时,它就会执行。这又是一个过于简化的模型,但要带走的核心概念是,就像在节点的事件循环中一样,每个进程都会获得它“需要”的内容,然后像节点应用程序这样的进程会尝试通过填写差距。
因此,当你的节点进程说它占用了 100% 的 cpu 时,这是不准确的,否则,其他任何事情都不会完成,系统也会崩溃。从本质上讲,它会占用所有可以使用的 CPU,但操作系统仍然会决定插入其他内容。
如果要添加执行相同 process.nextTick 的第二个节点进程,操作系统将尝试容纳这两个进程,并且根据每个节点进程的事件循环上要完成的工作量,操作系统将拆分工作相应地(至少在理论上,但实际上可能只会导致一切变慢和系统不稳定)。
再说一次,这过于简单化了,但希望它能让您了解发生了什么。话虽这么说,我不建议使用 process.nextTick 除非你知道你需要它,如果每 5 毫秒执行一次操作是可以接受的,那么使用 setTimeout 而不是 process.nextTick 将节省大量的 CPU 使用量。
希望能回答您的问题:D