何时使用线程池?

所以我对Node.js的工作原理有了一个了解:它有一个单独的侦听器线程,它接收一个事件并将其委托给一个工作池。 工作线程完成工作后通知监听器,然后监听器将响应返回给调用者。

我的问题是:如果我在Node.js中站起来一个HTTP服务器,并且在我的一个路由路径事件(例如“/ test / sleep”)上调用sleep,整个系统就会停止。 即使是单一的监听线程。 但我的理解是,这个代码发生在工作池上。

相比之下,当我使用Mongoose与MongoDB对话时,数据库读取是一种昂贵的I / O操作。 节点似乎能够将工作委托给线程,并在完成时接收回调; 从数据库加载所花费的时间似乎不会阻塞系统。

Node.js如何决定使用线程池线程与侦听器线程? 为什么我不能编写睡眠并只阻塞线程池线程的事件代码?


你对节点工作方式的理解是不正确的......但这是一种常见的误解,因为情境的现实实际上相当复杂,并且通常归结为像“节点是单线程”这样的简短短语,这会过度简化事物。

目前,我们将忽略通过集群和webworker线程的显式多处理/多线程,并且只谈论典型的非线程节点。

节点在单个事件循环中运行。 它是单线程的,你只能得到那一个线程。 所有你写的JavaScript都在这个循环中执行,如果在该代码中发生阻塞操作,那么它将阻塞整个循环,直到它完成时才会发生其他任何事情。 这是您通常听到的关于节点的单线程性质。 但是,这不是全貌。

通常用C / C ++编写的某些函数和模块支持异步I / O。 当你调用这些函数和方法时,它们在内部管理将呼叫传递给工作线程。 例如,当您使用fs模块来请求文件时, fs模块会将该调用传递给工作线程,并且该工作线程会等待其响应,然后将其呈现回事件循环,但没有它同时。 所有这些都是从节点开发人员那里抽象出来的,其中一些是通过使用libuv从模块开发人员中抽离出来的。

正如Denis Dollfus在评论中指出的(从这个答案到类似的问题),libuv用来实现异步I / O的策略并不总是一个线程池,特别是在http模块的情况下,出现了一种不同的策略在这个时候使用。 对于我们而言,注意如何实现异步上下文(通过使用libuv)以及由libuv维护的线程池是该库提供的多种策略之一以实现异步性,这一点非常重要。


在这个优秀的文章中,在一个与主要相关的切线上,关于节点如何实现异步性以及一些相关的潜在问题以及如何处理它们有了更深入的分析。 它大部分扩展了我上面写的内容,但另外它指出:

  • 您在您的项目中使用本地C ++和libuv的任何外部模块都可能使用线程池(请考虑:数据库访问)
  • libuv的默认线程池大小为4,并使用一个队列来管理对线程池的访问 - 结果是,如果有5个长时间运行的数据库查询全部同时进行,则其中一个(以及任何其他异步依赖于线程池的操作)将等待这些查询在他们开始之前完成
  • 您可以通过使用UV_THREADPOOL_SIZE环境变量增加线程池的大小来缓解这种情况,只要在需要和创建线程池之前完成: process.env.UV_THREADPOOL_SIZE = 10;

  • 如果您想在节点中使用传统的多处理或多线程,您可以通过内置的cluster模块或其他各种模块(如前面提到的webworker-threads获取它,也可以通过实施一些分块工作并手动使用setTimeoutsetImmediateprocess.nextTick来暂停您的工作,并在稍后的循环中继续以让其他流程完成(但不建议这样做)。

    请注意,如果您在JavaScript中编写长时间运行/阻止代码,那么您可能犯了一个错误。 其他语言将更有效率。


    所以我对Node.js的工作原理有了一个了解:它有一个单独的侦听器线程,它接收一个事件并将其委托给一个工作池。 工作线程完成工作后通知监听器,然后监听器将响应返回给调用者。

    这并不准确。 Node.js只有一个执行javascript的“worker”线程。 节点中有线程处理IO处理,但将其视为“工作人员”是一种误解。 实际上只有IO处理和节点内部实现的一些其他细节,但作为程序员,除了诸如MAX_LISTENERS之类的一些misc参数之外,您不能影响其行为。

    我的问题是:如果我在Node.js中站起来一个HTTP服务器,并且在我的一个路由路径事件(例如“/ test / sleep”)上调用sleep,整个系统就会停止。 即使是单一的监听线程。 但我的理解是,这个代码发生在工作池上。

    JavaScript中没有睡眠机制。 如果您发布了您认为“睡眠”意思的代码片段,我们可以更具体地讨论这一点。 例如,没有这样的函数可以调用来模拟python中的time.sleep(30)类的东西。 有setTimeout但这从根本上不睡觉。 setTimeoutsetInterval显式释放 ,而不是阻塞事件循环,以便其他位代码可以在主执行线程上执行。 你可以做的唯一事情就是使用内存中的计算来繁忙地循环CPU,这确实会使主执行线程陷入瘫痪,并使你的程序无法响应。

    Node.js如何决定使用线程池线程与侦听器线程? 为什么我不能编写睡眠并只阻塞线程池线程的事件代码?

    网络IO始终是异步的。 故事结局。 磁盘IO具有同步API和异步API,因此不存在“决定”。 node.js将根据您称之为同步与正常异步的API核心功能行事。 例如: fs.readFile vs fs.readFileSync 。 对于子进程,还有独立的child_process.execchild_process.execSync API。

    经验法则总是使用异步API。 使用同步API的合理原因是在网络服务中的初始化代码在监听连接之前,或者在不接受构建工具和网络请求的简单脚本中。


    这种误解仅仅是先发制人的多任务与合作多任务之间的区别......

    睡眠关闭了整个狂欢节,因为所有游乐设施都有一条线路,并且您关上了大门。 把它看作“一个JS解释器和其他一些东西”,并忽略这些线程......对于你来说,只有一个线程......

    ...所以不要阻止它。

    链接地址: http://www.djcxy.com/p/52525.html

    上一篇: When is the thread pool used?

    下一篇: process for cpu intensive task?