Node.js 在单个线程上运行,而脚本语言使用多个线程。
不是技术上的。Node.js 使用多个线程,但只有一个执行线程。后台线程用于处理 IO 以使所有异步功能正常工作。有效地处理线程是一件非常痛苦的事情,因此下一个最佳选择是在事件循环中运行,这样代码就可以在后台线程被 IO 阻塞时运行。
异步意味着无状态,并且连接是持久的,而同步则(几乎)相反。
不必要。您可以很容易地在异步系统中保存状态。例如,在 Javascript 中,您可以使用bind()
将 a 绑定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)非常慢,因此数据包之间存在相当长的时间。
例子
假设您有 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 绑定到单核,因此您无法利用多核处理器,除非您启动多个实例和代理请求。