本文摘自PHP中文网,作者青灯夜游,侵删。
虽说nodejs是单线程的,但是它还是容许多线程操作,下面本篇文章给大家从 Node 线程说起,谈谈Nodejs中的多线程操作,介绍一下worker_threads模板。本文测试使用环境:
系统:macOS Mojave 10.14.2
CPU:4 核 2.3 GHz
Node: 10.15.1
【推荐学习:《nodejs 教程》】
从 Node 线程说起
一般人理解 Node 是单线程的,所以 Node 启动后线程数应该为 1,我们做实验看一下。【推荐学习:《nodejs 教程》】
1 2 3 |
|
可以看到 Node 进程占用了 7 个线程。为什么会有 7 个线程呢?
我们都知道,Node 中最核心的是 v8 引擎,在 Node 启动后,会创建 v8 的实例,这个实例是多线程的。
- 主线程:编译、执行代码。
- 编译/优化线程:在主线程执行的时候,可以优化代码。
- 分析器线程:记录分析代码运行时间,为 Crankshaft 优化代码执行提供依据。
- 垃圾回收的几个线程。
所以大家常说的 Node 是单线程的指的是 JavaScript 的执行是单线程的,但 Javascript 的宿主环境,无论是 Node 还是浏览器都是多线程的。
Node 有两个编译器:
full-codegen:简单快速地将 js 编译成简单但是很慢的机械码。
Crankshaft:比较复杂的实时优化编译器,编译高性能的可执行代码。
某些异步 IO 会占用额外的线程
还是上面那个例子,我们在定时器执行的同时,去读一个文件:
1 2 3 4 5 6 7 |
|
线程数量变成了 11 个,这是因为在 Node 中有一些 IO 操作(DNS,FS)和一些 CPU 密集计算(Zlib,Crypto)会启用 Node 的线程池,而线程池默认大小为 4,因为线程数变成了 11。
我们可以手动更改线程池默认大小:
1 |
|
一行代码轻松把线程变成 71。
cluster 是多线程吗?
Node 的单线程也带来了一些问题,比如对 cpu 利用不足,某个未捕获的异常可能会导致整个程序的退出等等。因为 Node 中提供了 cluster 模块,cluster 实现了对 child_process 的封装,通过 fork 方法创建子进程的方式实现了多进程模型。比如我们最常用到的 pm2 就是其中最优秀的代表。
我们看一个 cluster 的 demo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
这个时候看下活动监视器:
一共有 9 个进程,其中一个主进程,cpu 个数 x cpu 核数 = 2 x 4 = 8 个 子进程。
所以无论 child_process 还是 cluster,都不是多线程模型,而是多进程模型。虽然开发者意识到了单线程模型的问题,但是没有从根本上解决问题,而且提供了一个多进程的方式来模拟多线程。从前面的实验可以看出,虽然 Node (V8)本身是具有多线程的能力的,但是开发者并不能很好的利用这个能力,更多的是由 Node 底层提供的一些方式来使用多线程。Node 官方说:
You can use the built-in Node Worker Pool by developing a C++ addon. On older versions of Node, build your C++ addon using NAN, and on newer versions use N-API. node-webworker-threads offers a JavaScript-only way to access Node’s Worker Pool.
但是对于 JavaScript 开发者,一直没有一个标准的、好用的方式来使用 Node 的多线程能力。
真 - Node 多线程
直到 Node 10.5.0 的发布,官方才给出了一个实验性质的模块 worker_threads 给 Node 提供真正的多线程能力。
相关阅读 >>
更多相关阅读请进入《nodejs》频道 >>

Vue.js 设计与实现 基于Vue.js 3 深入解析Vue.js 设计细节
本书对 Vue.js 3 技术细节的分析非常可靠,对于需要深入理解 Vue.js 3 的用户会有很大的帮助。——尤雨溪,Vue.js作者