javascript - Node.js单线程与并发

标签 javascript node.js multithreading concurrency parallel-processing

我有这个极小的Node.js服务器:

http.createServer(function (req, res) {
    var path = url.parse(req.url).pathname;

    if (path === "/") {
        res.writeHead(200, { 'Content-Type': 'text/plain' });
        res.write('Hello World');
        res.end('\n');
    } else {
        if (!handler.handle(path, req, res)) {
            res.writeHead(404);
            res.write("404");
            res.end();
        }
    }
}).listen(port);


做完一些基准测试后,我发现在高并发性(> 10000个并发连接)下,性能会大大下降。我开始更深入地研究Node.js并发性,搜索越多,我就越困惑。

我尝试从http范例中创建一个最小的示例,以便尝试更好地理解内容:
function sleep(duration) {
  return new Promise(resolve => setTimeout(resolve, duration));
}

var t0 = performance.now();

async function init() {
  await Promise.all([sleep(1000), sleep(1000), sleep(1000)]);

  var t1 = performance.now();
  console.log("Execution took " + (t1 - t0) + " milliseconds.")
}

init()
// Execution took 1000.299999985145 milliseconds.

据我了解,JavaScript在单个线程上运行。话虽这么说,但我不能像这样:
             |   1 second   |
Thread #One   >>>>>>>>>>>>>>
Thread #One   >>>>>>>>>>>>>>
Thread #One   >>>>>>>>>>>>>>

...显然,这没有意义。

那么,这是关于threadworker的术语问题吗?
             |   1 second   |
Thread #One  (spawns 3 workers)
Worker #One   >>>>>>>>>>>>>>
Worker #Two   >>>>>>>>>>>>>>
Worker #Three >>>>>>>>>>>>>>

???

Node.js如何是单线程的,但是能够并行处理三个函数?如果我对并行工作程序正确,http是否为每个传入的连接生成多个工作程序?

最佳答案

JavaScript程序中的线程通过为任务(事件)/作业队列提供服务来工作。从概念上讲,这是一个循环:从队列中拾取一个作业,将该作业运行至完成,拾取下一个作业,将该作业运行至完成。

考虑到这一点,您的带有 promise 的例子是这样的:

  • 使用输入文件运行Node.js会解析代码,并将作业排队以运行脚本中的顶级代码。
  • 主线程负责该工作并运行您的顶级代码,该代码如下:
  • 创建一些函数
  • 是否var t0 = performance.now();
  • 调用sleep(1000)
  • 创建一个 promise
  • 设置计时器以在大约1000毫秒内执行回调
  • 返回 promise
  • 再次调用sleep(1000)两次。现在有三个Promise,并且三个计时器回调大致安排在同一时间。
  • 等待这些 promise 的Promise.all。这将保存异步函数的状态并返回一个Promise(无用,因为没有使用init的返回值)。
  • 现在,运行顶级代码的作业已完成。
  • 线程现在处于空闲状态,正在等待作业执行(I/O完成,计时器等)。
  • 闲置大约一秒钟后,作业将排队以调用第一个计时器回调。这是如何发生的是特定于实现的:
  • 可能是主线程在循环检查工作队列的过程中还检查了计时器列表,以查看是否有任何计时器要运行。
  • 计时器系统可能有其自己的非JavaScript线程,该线程执行这些检查(或从OS机制获取回调),并在需要运行计时器回调时将作业排队。
  • 对于I/O(因为在您的示例中,计时器是I/O的替身),所以Node.js使用一个或多个单独的线程来处理来自操作系统的I/O完成并将它们的作业排队在其中JavaScript作业队列。
  • JavaScript线程接收该作业以调用计时器回调。它做到了,这实现了第一个sleep promise 。
  • 兑现 promise 会在“微任务”(或“ promise 工作”)中排队,以调用 promise 的兑现处理程序(then处理程序),而不是“宏任务”(标准工作)。现代JavaScript引擎在将它们排队的标准作业的末尾处理 promise 作业(即使主队列中已经有另一个标准作业等待完成- promise 作业会跳过主队列)。所以:
  • 完成第一个sleep promise 将使 promise 工作排队。
  • 在计时器回调的标准作业结束时,线程将选择promise作业并运行它。
  • promise作业触发Promise.all的逻辑内对实现处理程序的调用,该逻辑存储实现值并检查是否等待的所有 promise 都已兑现。在这种情况下,它们不是(两个仍然出色),因此没有其他可做的事情,并且 promise 工作已经完成。
  • 线程返回主队列。
  • 几乎立即,队列中有一个作业来调用下一个计时器回调(或者可能有两个作业来调用它们)。
  • 线程选择第一个线程并执行该任务,从而履行第二个sleep Promise,将一个Promise作业排队以运行其履行处理程序
  • 它拾取排队的 promise 工作,并按照Promise.all的逻辑调用实现处理程序,该逻辑存储实现值并检查是否所有 promise 都已实现。他们不是,所以它只是返回。
  • 它拾取并执行下一个标准作业,该作业完成了第三个sleep Promise,并为其实现处理程序排队了Promise作业。
  • 它拾取 promise 工作,并按照Promise.all的逻辑运行履行处理程序,该逻辑存储履行结果,查看所有 promise 均已兑现,并将 promise 工作排队以运行其履行处理程序。
  • 它拾取了新的Promise作业,从而推进了异步init函数的状态:
  • init函数执行var t1 = performance.now();,并显示t1t0之间的差异(大约一秒钟)。

  • (在该代码中)只涉及一个JavaScript线程。它正在运行一个循环,为队列中的作业提供服务。计时器可能有一个单独的线程,或者主线程可能只是在检查作业之间的计时器列表。 (我必须深入研究Node.js代码才能知道哪个,但是我怀疑后者,因为我怀疑它们正在使用特定于OS的调度功能。)如果是I/O完成而不是计时器,那么我一定要确保它们是由一个单独的非JavaScript线程处理的,该线程会响应来自操作系统的完成并将JavaScript线程的作业排队。

    关于javascript - Node.js单线程与并发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57070328/

    相关文章:

    javascript - 使用 firebase 并使用动态键名保存

    node.js - CRON + Nodejs + 多核 => 行为?

    javascript - ExpressJS - 带有路由分离的 Socket.IO

    javascript - 快速指南 hello.txt 不起作用

    java - 多线程模式下Log4j2自定义ContextDataInjector

    javascript - 使用 $.each 实例化多个对象

    javascript - CSS如何使用Auto设置margin-top

    c - 程序如何处理带有多个线程和信号的中断?

    java - 异步任务上的多个notifyObservers()不起作用

    javascript - Chrome 扩展程序消息监听器触发两次