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 绑定到单个内核,因此除非启动多个实例和代理请求,否则您无法利用多核处理器。