【问题标题】:Why is Node.js called single threaded when it maintains threads in thread pool?为什么Node.js在线程池中维护线程时称为单线程?
【发布时间】:2018-06-22 19:11:41
【问题描述】:

Node.js 维护一个事件循环,但默认情况下它也有四个线程来处理复杂的请求。当线程池中有更多可用线程时,这如何是单线程的?

另外,事件循环为复杂任务分配的线程是专用线程,那么它与其他多线程概念有何不同?

【问题讨论】:

  • 不是线程。它们是子进程。子进程、进程和线程是不同的东西。
  • 所以事件循环是唯一的一个线程,如果有任何阻塞 I/O 则由子进程处理!!我对么 ?你能帮我提供一些我可以正确阅读的链接吗?谢谢:)
  • 作为旁注,如果您喜欢并发编程,请查看 Go 编程语言。它是并发的,也是真正的多线程,但也是编译和静态类型的。 golang-book.com/books/intro/10
  • 我看错了你的问题。我不是在谈论 Node.js 维护的内部线程。我在谈论分叉的进程。 nodejs.org/dist/latest-v8.x/docs/api/…nodejs.org/dist/latest-v8.x/docs/api/…
  • duplicate 为什么 Node.js 是单线程的? [关闭] - 堆栈溢出https://stackoverflow.com › 问题 › 为什么-is-node-js-...

标签: node.js event-loop


【解决方案1】:

我会用简单明了的方式告诉它:

节点事件循环是单线程的,但其他进程不是。

混淆来自c++,Node使用下划线..默认情况下节点的Js部分是单线程的,但使用c++的线程池。所以如果需要异步 I/O 操作,我们有单线程 JS / 一个事件循环 NodeJs / 4 个 c++ 线程。

默认情况下,Node 使用 4 个线程池(PLUS 一个线程用于事件循环本身)。所以默认情况下 (5) 来自底层 c++ 系统。

CPU 可以包含一个或多个内核,这取决于您的服务器 == 金钱。

每个核心都可以有线程。观察您的活动监视器以发现这一点。

每个进程都有多个线程。

Node的多线程是由于该节点依赖于V8和libuv(C库)。

长话短说:-

节点事件循环本身是单线程的,但是有许多操作在事件循环之外完成,例如加密和文件系统 (fs)。如果您有两次对加密的调用,那么它们中的每一个都将到达每个线程(想象对加密的 3 个调用和对 fs 的 1 个调用,这些调用将为 4 个线程池中的每个线程分配一个)

最后:通过改变process.env.uv_threadpool_size的值,可以很容易的增加C-Library libuv线程池的默认线程数,默认为4。您还可以使用集群(推荐 PM2)来克隆事件循环,例如拥有多个事件循环,以防单线程不足以满足您的高负载应用程序。

所以没有人说明线程池是一个 c++ 的东西,它是由 nodeJs 控制的,主要不是开发人员......它仍然在问它是如何在拥有线程池时是单线程的!

希望能简化高级主题。

【讨论】:

    【解决方案2】:

    对于异步处理,Node.js 是作为实验显式创建的。与典型的基于线程的实现相比,在典型的 Web 负载下在单个线程上进行异步处理可以实现更高的性能和可扩展性。

    【讨论】:

    【解决方案3】:

    在您所指的上下文中,“单线程”意味着您的 Javascript 作为单线程运行。 没有两段 Javascript 可以同时运行,无论是字面意思还是时间切片(注意:从 2020 年开始,node.js 现在确实有 WorkerThreads,但这些与最初的讨论有所不同)。这极大地简化了 Javascript 开发,因为无需对在不同 Javascript 之间共享的 Javascript 变量进行线程同步,因为只有一个 Javascript 可以同时运行。

    话虽如此,node.js 确实在其实现中使用了内部线程。您提到的默认四个线程在线程池中用于磁盘 I/O。因为磁盘 I/O 通常是阻塞调用线程的 OS 级别的同步操作,并且 node.js 的设计中所有 I/O 操作都应该作为异步操作提供,所以 node.js 设计者决定实现异步接口通过使用线程池(在本机代码中)实现 fs 模块磁盘 I/O 接口(是的,在某些操作系统中存在非阻塞磁盘 I/O 操作,但 node.js 设计者决定不这样做使用它们)。这一切都发生在本机代码的幕后,并且不会影响您的 Javascript 仅在单个线程中运行的事实。

    这里总结了 node.js 中磁盘 I/O 调用的工作原理。假设已经有一个打开的文件句柄。

    1. Javascript 代码在现有文件句柄上调用 fs.write()
    2. fs 模块将参数打包到函数中,然后调用本机代码。
    3. 本机代码从线程池中获取一个线程并启动操作系统调用以将数据写入该文件
    4. 函数返回本机代码
    5. fs 模块从fs.write() 调用返回
    6. Javascript 继续执行(fs.write() 调用之后的任何语句
    7. 一段时间后,线程上的本机代码fs.write() 调用完成。它获取一个保护事件循环的互斥体,并在事件队列中插入一个事件。
    8. 当 Javascript 引擎完成执行它正在运行的任何 Javascript 流时,它会检查事件队列以查看是否有任何其他事件要运行。
    9. 当它在事件队列中找到一个事件时,它会将其从事件队列中移除并执行与该事件关联的回调,从而启动一个新的运行 Javascript 流。

    因为在当前 Javascript 流完成执行之前,永远不会对新事件采取行动,这就是 Javascript 获得事件驱动、单线程性质的地方,即使本机代码线程可能用于实现某些库函数。这些线程用于将阻塞操作变为非阻塞操作,但不影响 Javascript 执行本身的单线程性。

    这里的关键是 node.js 是事件驱动的。每个触发一些 Javascript 运行的新操作都会通过事件队列进行序列化,并且在当前的 Javascript 流完成执行之前不会处理下一个事件。


    在 node.js 架构中,让两段 Javascript 独立运行并同时运行的唯一方法是为每段使用单独的 node.js 进程。然后,它们将作为两个完全独立的操作运行,操作系统将分别管理它们。如果您的计算机至少有两个内核,那么它​​们实际上可以同时运行,每个内核都在自己的内核上。如果您的计算机只有一个内核,它们本质上将处于自己的进程线程中,并且操作系统会对它们进行时间切片(在它们之间共享一个 CPU)。

    【讨论】:

      【解决方案4】:

      默认情况下,您的 JavaScript 代码在单个线程上运行。

      但是,node.js 尝试使大多数长时间运行的调用异步。对于一些只涉及执行异步操作系统调用的人,但对于其他一些人,node.js 将在辅助线程上执行调用本身,同时继续运行其他 JS 代码。一旦异步调用终止,Js 回调或 Promise 处理程序将运行。

      【讨论】:

        猜你喜欢
        • 2022-01-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-11
        • 2014-07-21
        • 1970-01-01
        • 2015-08-18
        相关资源
        最近更新 更多