设为首页 - 加入收藏 ASP站长网(Aspzz.Cn)- 科技、建站、经验、云计算、5G、大数据,站长网!
热搜: 重新 试卷 文件
当前位置: 首页 > 运营中心 > 建站资源 > 优化 > 正文

深入理解Node.js 进程与线程(8000字长文彻底搞懂)(7)

发布时间:2019-08-15 16:27 所属栏目:21 来源:koala
导读:还是上面那个例子,我们在定时器执行的同时,去读一个文件: constfs=require('fs') setInterval(()={ console.log(newDate().getTime()) },3000) fs.readFile('./index.html',()={}) 线程数量变成了 11 个,这是因

还是上面那个例子,我们在定时器执行的同时,去读一个文件:

  1. const fs = require('fs') 
  2. setInterval(() => { 
  3.     console.log(new Date().getTime()) 
  4. }, 3000) 
  5.  
  6. fs.readFile('./index.html', () => {}) 

线程数量变成了 11 个,这是因为在 Node 中有一些 IO 操作(DNS,FS)和一些 CPU 密集计算(Zlib,Crypto)会启用 Node 的线程池,而线程池默认大小为 4,因为线程数变成了 11。

我们可以手动更改线程池默认大小:

  1. process.env.UV_THREADPOOL_SIZE = 64 

一行代码轻松把线程变成 71。

Libuv

Libuv 是一个跨平台的异步IO库,它结合了UNIX下的libev和Windows下的IOCP的特性,最早由Node的作者开发,专门为Node提供多平台下的异步IO支持。Libuv本身是由C++语言实现的,Node中的非苏塞IO以及事件循环的底层机制都是由libuv实现的。

libuv架构图

深入理解Node.js 进程与线程(8000字长文彻底搞懂)

在Window环境下,libuv直接使用Windows的IOCP来实现异步IO。在非Windows环境下,libuv使用多线程来模拟异步IO。

注意下面我要说的话,Node的异步调用是由libuv来支持的,以上面的读取文件的例子,读文件实质的系统调用是由libuv来完成的,Node只是负责调用libuv的接口,等数据返回后再执行对应的回调方法。

Node.js 线程创建

直到 Node 10.5.0 的发布,官方才给出了一个实验性质的模块 worker_threads 给 Node 提供真正的多线程能力。

先看下简单的 demo:

  1. const { 
  2.   isMainThread, 
  3.   parentPort, 
  4.   workerData, 
  5.   threadId, 
  6.   MessageChannel, 
  7.   MessagePort, 
  8.   Worker 
  9. } = require('worker_threads'); 
  10.  
  11. function mainThread() { 
  12.   for (let i = 0; i < 5; i++) { 
  13.     const worker = new Worker(__filename, { workerData: i }); 
  14.     worker.on('exit', code => { console.log(`main: worker stopped with exit code ${code}`); }); 
  15.     worker.on('message', msg => { 
  16.       console.log(`main: receive ${msg}`); 
  17.       worker.postMessage(msg + 1); 
  18.     }); 
  19.   } 
  20.  
  21. function workerThread() { 
  22.   console.log(`worker: workerDate ${workerData}`); 
  23.   parentPort.on('message', msg => { 
  24.     console.log(`worker: receive ${msg}`); 
  25.   }), 
  26.   parentPort.postMessage(workerData); 
  27.  
  28. if (isMainThread) { 
  29.   mainThread(); 
  30. } else { 
  31.   workerThread(); 

上述代码在主线程中开启五个子线程,并且主线程向子线程发送简单的消息。

由于 worker_thread 目前仍然处于实验阶段,所以启动时需要增加 --experimental-worker flag,运行后观察活动监视器,开启了5个子线程

深入理解Node.js 进程与线程(8000字长文彻底搞懂)

worker_thread 模块

worker_thread 核心代码(地址https://github.com/nodejs/nod...)

worker_thread 模块中有 4 个对象和 2 个类,可以自己去看上面的源码。

  • isMainThread: 是否是主线程,源码中是通过 threadId === 0 进行判断的。
  • MessagePort: 用于线程之间的通信,继承自 EventEmitter。
  • MessageChannel: 用于创建异步、双向通信的通道实例。
  • threadId: 线程 ID。
  • Worker: 用于在主线程中创建子线程。第一个参数为 filename,表示子线程执行的入口。
  • parentPort: 在 worker 线程里是表示父进程的 MessagePort 类型的对象,在主线程里为 null
  • workerData: 用于在主进程中向子进程传递数据(data 副本)

总结

多进程 vs 多线程

对比一下多线程与多进程:

深入理解Node.js 进程与线程(8000字长文彻底搞懂)

(编辑:ASP站长网)

网友评论
推荐文章
    热点阅读