Node.js 常被说成“单线程”,但这句话如果不展开,很容易误导。Node.js 在进程、线程和任务分发上其实提供了多种能力,只是它们各自解决的问题并不一样。这一篇会把 child_process、cluster 和 worker_threads 的选型边界讲清楚。
为什么还需要子进程、多进程和工作线程?
前面我们已经说过,Node.js 主线程非常适合组织 I/O,但它并不擅长独自扛住所有重任务。只要问题开始涉及:
- 长时间 CPU 计算;
- 外部命令执行;
- 多核利用;
- 进程隔离;
你就需要把“是不是还继续塞在主线程里”这件事重新评估。
也正因为如此,Node.js 提供了不同层次的并发能力。它们不是互相替代的关系,而是在解决不同类型的问题。
child_process 适合什么?
child_process 最典型的价值,在于把某个任务交给另一个进程去执行。它特别适合这些场景:
- 调用系统命令;
- 串联已有 CLI 工具;
- 运行另一个 Node 脚本;
- 把某段任务隔离到独立进程环境中。
它更像是在说:“这件事本来就更适合在另一个进程里完成。”所以它常用于脚本编排、自动化流程和外部工具整合。
它的重点不是共享内存或细粒度协作,而是进程级隔离和命令执行能力。
cluster 的历史定位是什么?
cluster 更偏服务端多实例扩展。你可以把它理解成:基于同一份 Node 程序,派生出多个工作进程,让它们共同处理请求,以便更好利用多核资源。
它的历史意义很大,因为在较早阶段,很多 Node 服务端扩展方案都围绕它展开。但今天讨论 cluster 时,心态应该更务实一些:它仍然是理解 Node 服务多进程利用的重要一环,但并不意味着所有现代扩展都必须围绕它。
更重要的是理解它解决的本质问题:当一个服务需要利用多核时,单一进程不够,就需要多进程协作。
worker_threads 为什么会出现?
如果说 child_process 更偏外部进程与隔离,worker_threads 则更像是 Node 在运行时内补上的“主线程以外的计算执行能力”。
它特别适合这些情况:
- 需要做明显的 CPU 密集型任务;
- 不想让主线程长期被重计算拖住;
- 希望在同一进程体系内完成更细粒度的任务分发。
你可以把它理解成:Node 主线程继续负责调度和 I/O,而重计算逻辑被有意识地分派给工作线程。
这不是说工作线程天然高级,而是说它适合那些“必须并行计算,但又不只是调用一个外部命令”的问题。
并发能力到底该怎么选?
做选择时,最重要的不是记住“哪个更新”,而是先问清楚任务本身是什么类型:
如果你要调用外部命令或独立脚本
优先想到 child_process。因为问题本来就更像“把任务交给另一个程序”。
如果你要提升服务多核利用率
优先理解多进程模型和 cluster 这类思路。因为此时重点是服务实例扩展,而不是某一段单独计算。
如果你要处理重 CPU 计算
优先考虑 worker_threads。因为问题本质是主线程不该被长时间计算压住。
如果任务本来就很轻
那最好的方案可能是什么都别加。并发能力不是越多越好,复杂度和资源开销本身也是成本。
总结
这一篇我们把 Node.js 的几种典型并发能力放到了一起理解:child_process 解决外部命令和进程隔离,cluster 解决服务多进程扩展,worker_threads 则更适合 CPU 密集任务的主线程卸载。
真正有价值的不是背结论,而是先看问题类型,再选对应并发手段。Node.js 从来不是不能并发,而是需要你更有意识地决定并发发生在哪一层。
下一篇我们会把视角转向调试和诊断,让前面这些运行时知识真正能落到排障与性能分析上。