如何理解Node.js的单线程?
我们常说, Node.js 是单线程的,这句话对新人有很大的误导作用。首先要明确:Node.js 程序并非「单线程」,证明代码如下:
let end = Date.now() + 1000 * 10
for (; Date.now() < end;) {}
运行之后,到活动监视器中搜索 Node,结果如下:

看到没,一个 Node 程序有 7 个线程。到这里,你可能会很困惑,这究竟是怎么回事?其实正确的说法应该是:
单线程的指的是 JavaScript 的执行是单线程的,但 Javascript 的宿主环境并非单线程。
怎么理解这句话呢?其实当你用 node xxx.js 运行程序的时候,操作系统会启动下面 7 个线程:
- 1 个 Javascript 主线程用于执行用户代码
- 1 个 watchdog 监控线程用于处理调试信息
- 1 个 v8 task scheduler 线程用于调度任务优先级,加速延迟敏感任务执行
- 4 个 v8 线程用来执行代码调优与 GC 等后台任务
所以说,我们的 Node 程序中包含的线程实际上是:JavaScript 的宿主环境需要的线程 + JavaScript 的执行线程。
到这里是不是有种豁然开朗的感觉了?别急,我再加一行代码:
require('fs').readFile(require.main.filename, () => {})
let end = Date.now() + 1000 * 10
for (; Date.now() < end;) {}
这个时候活动监视器的结果如下:

WTF?怎么多了 4 个线程?这是怎么回事?其实原因就出现在第一行代码上:
require('fs').readFile(require.main.filename, () => {})
这行代码是异步操作,而 Node 是「异步非阻塞」的语言,如果还是 7 个线程的话,就意味着 JavaScript 主线程也要执行 I/O 操作,从而造成了阻塞。所以 libuv 创建了线程池,默认情况下线程池里面有 4 个线程,所以我们看到的结果是 11。
也就是说,代码里面那些异步操作,全部由线程池来接管,JavaScript 主线程不参与进去,只是执行同步代码而已。下面是 Node 进程结构图:

如果全部是同步代码,那么只会开启 7 个线程,如果存在异步 I/O 操作,则默认会开启 11 个线程。为什么说是「默认」呢?因为 uv_thread_pool 的容量是可以改的,只要设置环境变量 UV_THREADPOOL_SIZE 即可。例如下面的代码把线程池的容量改成 1:
process.env.UV_THREADPOOL_SIZE = 1
require('fs').readFile(require.main.filename, () => {})
let end = Date.now() + 1000 * 10
for (; Date.now() < end;) {}
这个时候就只有 8 个线程了:

所以,Node.js 程序并非单线程,只不过主线程是单线程的,所有的异步 I/O 操作由 libuv 的线程池中的线程进行处理,然后把运行结果通过回调的方式通知到主线程。
以上关于如何理解Node.js的单线程?的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » 如何理解Node.js的单线程?
微信
支付宝