Node.js 在单线程上运行,而脚本语言则使用多线程。
技术上不行。 Node.js 使用多个线程,但只有一个执行线程。后台线程用于处理 IO,以使所有异步功能发挥作用。有效地处理线程是一件非常痛苦的事情,因此下一个最佳选择是在事件循环中运行,以便在后台线程在 IO 上被阻塞时代码可以运行。
异步意味着无状态,并且连接是持久的,而同步则(几乎)相反。
不必要。您可以非常轻松地在异步系统中保留状态。例如,在 JavaScript 中,您可以使用bind()
绑定一个this
到一个函数,从而在函数返回时显式地保留状态:
function State() {
// make sure that whenever doStuff is called it maintains its state
this.doStuff = this.doStuff.bind(this);
}
State.prototype.doStuff = function () {
};
异步意味着不等待操作完成,而是注册侦听器。这种情况在其他语言中经常发生,尤其是任何需要接受用户输入的语言。例如,在 Java GUI 中,您不会阻止等待用户按下按钮,而是向 GUI 注册一个侦听器。
我与该主题相关的第二个问题也是最后一个问题是:
JavaScript 可以成为同步语言吗?
从技术上讲,所有语言都是同步的,甚至 Javascript 也是如此。然而,Javascript 在异步设计中工作得更好,因为它被设计为单线程。
基本上有两种类型的程序:
- CPU 限制 - 让它运行得更快的唯一方法是获得更多的 CPU 时间
- IO 限制 - 花费大量时间等待数据,因此更快的处理器并不重要
视频游戏、数字计算器和编译器受 CPU 限制,而 Web 服务器和 GUI 通常受 IO 限制。 Javascript 相对较慢(因为它非常复杂),因此它无法在 CPU 密集型场景中竞争(相信我,我已经编写了相当多的 CPU 密集型 Javascript)。
Javascript 不是根据类和对象进行编码,而是根据可以串在一起的简单函数进行编码。这在异步设计中非常有效,因为可以编写算法来增量处理数据。IO(尤其是网络 IO)非常慢,因此数据包之间有相当多的时间。
Example
假设您有 1000 个实时连接,每个连接每毫秒传送一个数据包,处理每个数据包需要 1 微秒(非常合理)。我们还假设每个连接发送 5 个数据包。
在单线程同步应用程序中,每个连接都将被串行处理。所花费的总时间为 (5*1 + 5*.001) * 1000 毫秒,即 ~5005 毫秒。
在单线程异步应用程序中,每个连接都将并行处理。由于每个数据包需要 1 毫秒,而处理每个数据包需要 0.001 毫秒,因此我们可以处理数据包之间的每个连接的数据包,因此我们的公式变为:1000*.001 + 5*1 毫秒,即~6 毫秒。
解决这个问题的传统方法是创建更多线程。这解决了 IO 问题,但是当连接数增加时,内存使用量(线程消耗大量内存)和 CPU 使用量(将 100 个线程复用到 1 个核心比 1 个线程复用到 1 个核心更难)也随之增加。
然而,也有缺点。如果您的 Web 应用程序碰巧还需要进行一些繁重的数字运算,那么您就是 SOL,因为在您处理数字时,连接需要等待。线程解决了这个问题,因为当数据准备好等待 IO 的线程时,操作系统可以换出 CPU 密集型任务。此外,node.js 绑定到单核,因此除非启动多个实例和代理请求,否则您无法利用多核处理器。