关于nodejs线程的一些困惑

Posted

技术标签:

【中文标题】关于nodejs线程的一些困惑【英文标题】:Some confusion about nodejs threads 【发布时间】:2016-09-15 22:48:41 【问题描述】:

我对@9​​87654321@ 线程有些困惑。

据我所知,nodejs 在单线程中运行。如果调用了异步函数,nodejs 将创建一个新线程来运行这些异步代码。异步代码完成后。 event loop 将获得异步结果并在另一个新线程中触发回调函数。

为了测试,我进行了如下代码测试:

var fs = require("fs");

fs.readFile('package.json','utf-8', function (err, data) 
    if(err)
        console.log(err)
    else
        console.log(data);
);

console.log("before loop");
while(true);
console.log("after loop");

我的预期结果是:

循环前 -- package.json 内容 --

其实得到:

循环前

我可能在某些地方错了。

问题

    为什么while(true) 屏蔽了readFile() 或者屏蔽了它的回调函数? 这些线程之间是什么关系?

【问题讨论】:

回调的神奇之处在于闭包。当您将一个函数传递给readFile 时,它会使该函数保持活动状态,并使闭包中捕获的所有变量保持活动状态。稍后,node 可以突然调用该函数(假设您已经返回到 node 的事件循环),从程序员的角度来看,这非常方便,因为闭包在调用之间携带变量。可以从您显示的代码示例返回,因为任何必要的变量都将保持活动状态,并且可以从需要它们的任何函数访问。 【参考方案1】:

你说得对,nodejs 是在单线程中运行的。您可以启动单独的进程并在这些进程之前放置一个负载平衡器以利用多核处理器,但这并未在节点本身中实现。

但是,节点不会为它遇到的每个异步函数启动一个线程。它将这些函数调用放入不断运行的事件循环中。这里重要的是事件循环是非阻塞的,这意味着函数调用执行的顺序无法保证——这就是为什么回调是所有 node.js 程序的基础的原因。这意味着,例如,当一个函数正在等待在文件系统中找到文件并读取到缓冲区时,事件循环不会停止一切以等待它完成并继续下一次调用。但绝不是这种多线程行为。事件循环不会并行执行任何事情,它只是在任务之间不断切换。这只是多任务处理:)

在您的示例中,while(true) 阻塞了事件循环,因为它不是异步函数调用。不要忘记,并非 javascript 中的所有内容都是异步的——只有函数调用可以是异步的,而其他语言结构(例如循环或条件)则不能。因此,在您的示例中,您运行添加到事件循环并启动的异步文件阅读器。当文件系统在做它的事情时,事件循环无事可做,并转移到 console.log("before loop") 消息。之后,您使用 while(true) 循环阻止事件循环。这意味着事件循环不能将其搁置并再转到文件读取器。

希望能解决问题。这里的主要内容是,并非 javascript 中的所有内容都是或必须是异步的,但是处理不同设备(例如文件系统或网络)的大多数耗时操作都是以非阻塞方式实现的。异步函数只有在它使用一些外部组件时才有意义 - 你不会使用异步平方根函数,因为它会阻止事件循环。这就是为什么不建议将 node.js 用于繁重的计算操作的原因,例如音频/视频编码 - 异步优势在这里没有意义。

【讨论】:

谢谢你,塔达斯。你的意思是我不在异步函数中的代码将在event-loop中以同步方式运行,而调用的异步函数将在event-loop中以异步方式运行? 我的意思是,while(true) 是一个同步操作,无法完成或暂停,因此事件循环无法返回到 filereader 并且程序冻结。同步/异步函数没有单独的事件循环,它们都在同一个域中。【参考方案2】:

我在这里找到了一些其他很棒的答案:

http://blog.carbonfive.com/2013/10/27/the-javascript-event-loop-explained/ What exactly is a Node.js event loop tick?

此回调函数的调用作为调用堆栈中的初始帧,由于 JavaScript 是单线程的,进一步的消息轮询和处理将暂停,等待堆栈上的所有调用返回。后续(同步)函数调用将新的调用帧添加到堆栈中(例如,函数 init 调用函数 changeColor)。

它再次解决了我的困惑!我现在更清楚了!

【讨论】:

以上是关于关于nodejs线程的一些困惑的主要内容,如果未能解决你的问题,请参考以下文章

关于 Project Reactor 的 flatMap 中的线程的困惑

关于 C# 中的 lock 语句的困惑

关于node.js内部异步I/O机制的困惑

关于 QThread、QObject、线程关联和事件循环的混淆

NodeJS - Event Loop 模型

无法在函数内调用非内置函数