首要仍是一如既往的打个广告。假如对前端八股文感兴趣,能够看这儿,假如对react源码感兴趣,能够看这儿。感兴趣的朋友别忘了star哦,总有你要的干货[狗头]。

纲要

  • Node根本架构
  • 进程、线程的基础常识
  • CPU线程调度
  • Node是否单线程?
  • libuv线程池。线程是不是越多越好?

Node根本架构

【Node】Node是否单线程?实例解说线程池原理

  • 最上层是咱们自己编写的javascript代码
  • 中心的Node JS是Node供给给咱们的规范库,比方fs,path。一起也是咱们和C++衔接的桥梁。
  • V8:开源的js引擎。能够在浏览器之外履行javascript代码。因而这儿是真实履行咱们自己写的javascript代码的地方
  • libuv库。C++开源项目,答应node.js拜访操作系统、底层文件系统。答应拜访网络,还能够处理并发

进程、线程的基础常识

关于进程和线程的基础常识,网上有很多,这儿就不赘述了。

  • 进程。一个进程中会有多个线程。是操作系统进行资源分配(包括cpu、内存、磁盘IO等)的最小单位
  • 线程。cpu调度和分配的根本单位
  • 串行: 多个使命,履行完一个再履行另一个。
  • 并发: 多个线程在单个CPU内核运转,同一时刻一个线程运转,系统不断切换线程,看起来像一起运转,实践上是线程不断切换。
  • 并行: 每个线程分配给独立的CPU内核,线程一起运转。

这儿咱们以Node为例,新建一个index.js,内容如下:

const express = require('express')
const app = express();
app.get('/slow', (req, res) => {
    let startTime = Date.now();
    while(Date.now() - startTime < 10000){}
    res.send('slow request')
})
app.get('/fast', (req, res) => {
    res.send('fast request')
})
app.listen(3000)

当咱们履行node index.js时,就启动了一个进程实例。这便是一个node服务。留意,这儿只要一个进程实例。一起也是咱们的一个应用。这个进程实例包括一个 主线程(也称事情循环线程) 以及N个 线程池 中的线程。

实践上,咱们自己编写的nodejs代码简直都是在主线程当中运转的,除了咱们手动创立的worker thread外。这就意味着咱们自己编写的代码有可能会堵塞Node的主线程。Node供给的规范函数中,有些是直接调用操作系统底层操作,比方http模块。有些是在线程池中由线程池调度履行,比方crypto模块的pbkdf2函数,fs模块等,在线程池中的操作以及调用操作系统底层的操作不会堵塞主线程。

CPU线程调度

假定核算机只要一个CPU,则在任意时刻只能履行一条机器指令,每个线程只要取得CPU的使用权才干履行指令。线程调度是指按照特定机制为多个线程分配CPU的使用权。

有两种调度模型:分时调度模型和抢占式调度模型

  • 分时调度模型是指让一切的线程轮番取得CPU的使用权,并且均匀分配每个线程占用的CPU的时刻片
  • 抢占式调度模型,是指让优先级高的线程优先占用CPU履行。

关于两种调度模型,自行了解即可。这儿需求留意咱们常说的多线程并发履行,指的是从微观上看,各个线程轮番取得CPU的使用权,分别履行各自的使命。

假定有8个线程处于安排妥当状况等候履行,一起假定每个线程需求耗时500毫秒履行使命,咱们的CPU只要4个内核。那么CPU在调度这8个线程时,理论上来说,是由4个内核轮番调度履行这8个线程,每个线程履行一个时刻片后,就持续履行另一个线程。均匀每个内核需求履行2个线程。因而,这8个线程简直是一起完结的,耗时1000毫秒。当然这只是理论值,真实情况远比这个时刻要大,毕竟CPU切换线程也是有开销的,一起一台核算机的线程也不仅仅只要8个。

Node是否单线程?

Node事情循环线程是单线程,也便是履行咱们自己写的代码的线程是单线程。这意味着咱们的程序只能在一个CPU内核上运转。假如咱们核算机的CPU有多个核,那么node就不会充分利用到这些优势。换句话说,咱们的程序就没法快速的运转,因为它是单线程。

但是Node规范库中供给的一些函数实践上并不是单线程。Node内部供给的一些函数运转在咱们的事情循环线程之外,也便是其他线程中。

因而,简略的说node是单线程并不完全正确。

怎么证明事情循环线程是单线程?

以下面的代码为例:

const express = require('express')
const app = express();
app.get('/slow', (req, res) => {
    let startTime = Date.now();
    while(Date.now() - startTime < 10000){}
    res.send('slow request')
})
app.get('/fast', (req, res) => {
    res.send('fast request')
})
app.listen(3000)

经过node index.js启动这个服务后,先拜访http://localhost:3000/slow然后当即拜访http://localhost:3000/fast。能够猜想一下履行成果。

很明显,/slow这个恳求必定耗时在10s。/fast恳求耗时多少秒?会不会被堵塞?

【Node】Node是否单线程?实例解说线程池原理

【Node】Node是否单线程?实例解说线程池原理

从履行成果来看,/fast恳求被前面的/slow恳求堵塞的。假如手速不够快,也能够经过代码建议恳求:

【Node】Node是否单线程?实例解说线程池原理

能够看到10秒后先输出 slow 耗时,然后紧接着输出fast 耗时

所以,事情循环线程是单线程,且很简单被咱们所写的耗时长的使命堵塞的。假定咱们有1000个恳求一起建议,顺次到达服务器,每个恳求耗时5ms,理论上第1000个恳求呼应要5000ms。

怎么证明Node不是单线程

前面咱们说过,Node供给的内置模块中,有些是在线程池中调度履行的。比方crypto模块。先来看下面的代码:

const crypto = require('crypto')
const startTime = Date.now();
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('1 恳求耗时:', Date.now() - startTime)
})
console.log('end')

履行多几次检查成果:

【Node】Node是否单线程?实例解说线程池原理
能够看出,一个pbkdf2函数需求耗时在520毫秒左右。假如nodejs是单线程的,那么履行两次pbkdf2函数应该一共耗时在1000毫秒左右才对,比方下面的代码:

const crypto = require('crypto')
const startTime = Date.now();
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('1 恳求耗时:', Date.now() - startTime)
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('2 恳求耗时:', Date.now() - startTime)
})
console.log('end')

这儿需求留意startTime是个常量,一开始就现已初始化了。假如nodejs是单线程,这儿的履行成果应该是首要履行完第一个pbkdf2,耗时520毫秒左右。然后履行第二个pbkdf2函数,耗时1000毫秒左右。 现在咱们屡次履行,检查成果:

【Node】Node是否单线程?实例解说线程池原理

上面的成果有两点需求留意:

  • 打印次序是随机的,并不是先打印第一个再打印第二个。
  • 两个函数的履行耗时都在530毫秒左右。

从上面的成果能够看出,pbkdf2的履行是多线程的,谁先履行完就先履行谁的回调。

因而,精确的说,node并不是单线程的。但node的主线程(事情循环线程)是单线程的,也便是履行咱们自己写的代码的线程是单线程的。而node规范库中的一些函数是多线程履行的。

crypto.pbkdf2函数本身是在线程池中履行的,而他的回调是在事情循环线程(主线程)履行的

libuv线程池

前面简略介绍了crypto.pbkdf2函数的履行并不是单线程的,这节咱们将简略看下根本原理。

下面图示是crypto.pbkdf2函数的实践运转情况

【Node】Node是否单线程?实例解说线程池原理

libuv会将一些贵重的核算操作放在事情循环线程之外,也便是线程池中履行。默许情况下,libuv会在线程池中创立4个线程,可用于核算密集型使命。因而这意味着除了事情循环线程之外,还有其他四个线程可用于在咱们的应用程序内履行贵重的操作。Node规范库的大部分函数都会充分利用这个线程池,比方crypto.pbkdf2函数,fs模块等。

很明显,node不是真实的单线程,因为node还使用其他线程来履行一些核算密集型的使命。

libuv线程池中有4个线程,意味着,最多能一起处理4个使命。比方下图所示,假如咱们调用了6次crypto.pbkdf2函数,意味着咱们有6个使命需求线程池调度处理。但线程池只要4个线程,最多只能一起处理4个使命,剩余的2个使命需求排队。比方假如PBKDF2#3先处理完结,则PBKDF2#5递补进入线程池调度处理。

【Node】Node是否单线程?实例解说线程池原理

实例分析

留意,我使用的这台核算机CPU有4个内核。下面一切demo及其成果都是在这台核算机上跑的。

4个线程,4个使命

const crypto = require('crypto')
const start = Date.now();
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 1: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 2: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 3: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 4: ', Date.now() - start);
})
console.log('end')

多履行几次,检查成果:

【Node】Node是否单线程?实例解说线程池原理

能够得出以下几点定论:

  • 回调函数的履行次序是不确定的
  • 四个函数的履行耗时大约在550毫秒左右

履行进程如下图所示

【Node】Node是否单线程?实例解说线程池原理

这儿咱们调用了四次crypto.pbkdf2函数,也便是有4个使命需求履行。这4个使命进入线程池使命行列处理。因为线程池有4个线程,因而每个使命刚好能够分配给一个线程一起履行。我这台核算机CPU有4个内核,这4个内核轮番履行4个线程,均匀每个内核履行1个线程,均匀耗时550毫秒左右。因而能够看到4个回调函数简直一起履行完结,耗时差不多。

4个线程,6个使命

考虑下面代码的履行成果:

const crypto = require('crypto')
const start = Date.now();
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 1: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 2: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 3: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 4: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 5: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 6: ', Date.now() - start);
})
console.log('end')

控制台屡次履行,能够得到下面的成果:

  • 过了大约550毫秒,简直一起打印前面4个crypto.pbkdf2函数的回调。并且4个使命的打印次序是随机的。
  • 过了大约1080毫秒,简直一起打印最终2个crypto.pbkdf2的回调。并且这2个使命的打印次序也是随机的。

【Node】Node是否单线程?实例解说线程池原理

履行进程如下图所示:

【Node】Node是否单线程?实例解说线程池原理

这儿咱们调用了6次crypto.pbkdf2,这6个使命参加线程池使命行列等候处理。因为线程池只要4个线程,每个线程只能履行一个使命。因而前面4个使命优先履行,后边两个排队等候履行。只要线程池中的线程闲暇了,才干履行后边两个使命。这便是为啥咱们会看到前面4个简直一起完结,然后才输出最终2个。

5个线程,6个使命。线程池中的线程越多越好吗?

咱们能够经过process.env.UV_THREADPOOL_SIZE修改线程池中的线程数量。比方下面的代码将线程池里面的数量调整为5。

process.env.UV_THREADPOOL_SIZE = 5;

咱们仍是以上面的代码为例,在履行下面的代码前,能够考虑下履行成果会是怎样?

process.env.UV_THREADPOOL_SIZE = 5;
const crypto = require('crypto')
const start = Date.now();
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 1: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 2: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 3: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 4: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 5: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 6: ', Date.now() - start);
})
console.log('end')

在履行前,能够考虑一下履行成果是怎样的。

  • 大约687毫秒后,简直一起打印前面5个使命。打印次序随机
  • 大约1200毫秒后,才输出第6个使命。

这个成果需求留意的是,前面5个使命,每个使命的履行时刻大约在687毫秒,明显比之前的使命(550毫秒)多了一些。另外,这次是5个使命简直一起完结。

【Node】Node是否单线程?实例解说线程池原理

履行进程如下图所示:

【Node】Node是否单线程?实例解说线程池原理

这次,咱们的线程池中有5个线程,能一起处理5个使命。这儿咱们调用了6次crypto.pbkdf2,有6个使命需求线程池处理。但线程池只要5个线程,意味着一次最多只能一起处理5个使命。因而前面5个使命分配给了线程池中的每个线程。但因为我的CPU只要4个内核,却需求一起处理5个线程,CPU是轮番调度履行每个线程的,每个线程在履行完一个时刻片后,就切换到另一个线程,均匀每个内核需求处理1.25个线程。理论情况下,一个线程履行一个crypto.pbkdf2耗时550毫秒,1.25个线程大约是687毫秒。这就解释了前面5个线程简直一起完结,且均匀耗时687毫秒。最终一个线程需求的时刻为687毫秒+本身履行的550毫秒,大约便是1237毫秒左右。

从上面能够看出,因为CPU资源内核有限,一起CPU是轮番履行每个线程,因而,线程池中的线程并不是越多越好。

了解了这些后,咱们能够修改线程池巨细为6,然后履行12个使命,比方下面的代码,考虑一下履行成果怎么?

process.env.UV_THREADPOOL_SIZE = 6;
const crypto = require('crypto')
const start = Date.now();
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 1: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 2: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 3: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 4: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 5: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 6: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 7: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 8: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 9: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 10: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 11: ', Date.now() - start);
})
crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
    console.log('crypto 12: ', Date.now() - start);
})
console.log('end')

那些直接调用操作系统底层的模块

前面咱们知道nodejs供给的crypto.pbkdf2函数是在线程池中履行的。当然,nodejs供给的其他内置模块并不都是在线程池中履行的。

本节就来讨论nodejs内部怎么处理网络恳求。经过代码验证nodejs中网络恳求并不是在线程池中处理的。首要咱们先经过一个简略的demo测试一下恳求www.baidu.com这个网址耗时多长时刻

const https = require('https')
const startTime = Date.now();
const doRequest = (num) => {
    https.request('https://www.baidu.com/', res => {
        res.on('data', (data) => {
        })
        res.on('end', () => {
            console.log(`${num}:`, Date.now() - startTime)
        })
    }).end();
}
doRequest(1);

控制台屡次履行,能够看到耗时在300毫秒左右。

【Node】Node是否单线程?实例解说线程池原理

假如咱们屡次调用doRequest,如下图所示:

const https = require('https')
const startTime = Date.now();
const doRequest = (num) => {
    https.request('https://www.baidu.com/', res => {
        res.on('data', (data) => {
        })
        res.on('end', () => {
            console.log(`${num}:`, Date.now() - startTime)
        })
    }).end();
}
doRequest(1);
doRequest(2);
doRequest(3);
doRequest(4);
doRequest(5);
doRequest(6);
doRequest(7);
doRequest(8);
doRequest(9);
doRequest(10);
doRequest(11);
doRequest(12);
doRequest(13);

控制台屡次履行,能够看到成果如下:

  • 这儿调用了13次,简直每个日志都是一起输出的
  • 和屡次履行crpto.pbkdf2函数的成果不同,每个doRequest的耗时简直都相同,在280毫秒左右。这个和咱们之前在线程池中看到的行为是完全不相同的,并不像crypto.pbkdf2相同需求排队等候线程池处理。

【Node】Node是否单线程?实例解说线程池原理

因而,node供给的内置规范库中,一些函数利用了libuv的线程池。相同,也存在一些函数是直接调用libuv中内置的一些操作系统代码。在本例中,咱们调用https.request建议一个恳求,libuv发现咱们是在建议HTTP恳求,libuv没法直接处理触及网络恳求的操作。因而,libuv将恳求托付给操作系统履行。所以实践上是咱们的操作系统履行真实的http恳求,libuv只是被用来宣布恳求,然后等候操作系统回来恳求的成果。

因为libuv将作业托付给操作系统,所以操作系统自己决定是否需求创立新的线程。一起,因为是操作系统宣布恳求,所以内部不会堵塞咱们事情循环中的javascript代码。

有趣的事情循环

先来看下fs模块读取文件的耗时:

const fs = require('fs')
const startTime = Date.now();
fs.readFile('test.js', 'utf8', () => {
    console.log('FS耗时:', Date.now() - startTime)
})

实践上,fs模块读取文件仍是非常快速的,耗时1毫秒

【Node】Node是否单线程?实例解说线程池原理

然后再来看下面的代码:

const https = require('https')
const crypto = require('crypto')
const fs = require('fs')
const startTime = Date.now();
const doRequest = (num) => {
    https.request('https://www.baidu.com/', res => {
        res.on('data', (data) => {
        })
        res.on('end', () => {
            console.log(`doRequest ${num} 耗时:`, Date.now() - startTime)
        })
    }).end();
}
const doHash = (num) => {
    crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
        console.log(`Hash ${num} 耗时: `, Date.now() - startTime);
    })
}
doRequest(1)
fs.readFile('test.js', 'utf8', () => {
    console.log('FS 耗时:', Date.now() - startTime)
})
doHash(1)
doHash(2)
doHash(3)
doHash(4)

控制台履行屡次检查成果:

  • 每次都是doRequest的回调函数先履行
  • 每次FS打印前,前面都有一个Hash先打印
  • FS耗时均匀在560毫秒左右,而不是上面的1毫秒。

【Node】Node是否单线程?实例解说线程池原理

为什么会呈现这种情况?前面咱们说过fs module和crypto.pbkdf2都使用了线程池履行操作。HTTP模块直接使用底层操作系统履行操作。在解释这个履行成果前,咱们先来了解下fs.readFile的内部履行流程。

当咱们调用fs.readFile时,node会读取两次磁盘。第一次从磁盘中获取文件信息,比方文件巨细,才干知道应该恳求多大的内存读取文件内容。当获取到文件信息后,node会持续读取磁盘,这次真实的获取文件内容。咱们都知道I/O操作是比较耗时的,node 线程不可能会一向等候I/O操作完结,中心没事可做,形成资源浪费。因而在读取磁盘等候读取成果时,node会将操作挂起,让线程持续处理其他使命。比及成果回来时,再让线程持续处理读取成果。

结合上述常识,上面代码履行流程如下:

【Node】Node是否单线程?实例解说线程池原理

首要要认识到http模块调用根本不触及线程池,它直接调用底层操作系统。它宣布恳求,一旦咱们得到呼应,就会将成果回来到咱们的事情循环线程中。

然后咱们调用fs模块读取文件,一起调用了4个doHash。相当于有5个使命需求线程池处理,而线程池只要4个线程,一次最多只能处理4个使命。因而首要被处理的是fs以及前面三个pbkdf2函数。假定fs分配给了thread#1thread#1第一次读取磁盘,等候磁盘回来成果,因为I/O比较耗时,thread#1将fs使命挂起,转而持续处理其他使命,也便是pbkdf2#4这个使命。过了一会,磁盘读取完结,将成果回来给线程池中处理,因为此刻线程池中有4个使命在处理,fs的读取成果只能排队等候处理

此刻,线程池状况如下:

【Node】Node是否单线程?实例解说线程池原理

过了270毫秒,http恳求完结,成果回来给事情循环线程处理。此刻打印doRequest

过了大约560毫秒,有个pbkdf2使命处理完结,成果回来给事情循环线程处理,此刻打印一个doHash。

有一个线程闲暇了,能够持续处理fs的读取成果,因为文件操作只需求1毫秒,因而打印fs。

最终输出后边三个doHash。

现在,让咱们将线程池巨细改成5

process.env.UV_THREADPOOL_SIZE = 5;
const https = require('https')
const crypto = require('crypto')
const fs = require('fs')
const startTime = Date.now();
const doRequest = (num) => {
    https.request('https://www.baidu.com/', res => {
        res.on('data', (data) => {
        })
        res.on('end', () => {
            console.log(`doRequest ${num} 耗时:`, Date.now() - startTime)
        })
    }).end();
}
const doHash = (num) => {
    crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
        console.log(`Hash ${num} 耗时: `, Date.now() - startTime);
    })
}
doRequest(1)
fs.readFile('test.js', 'utf8', () => {
    console.log('FS 耗时:', Date.now() - startTime)
})
doHash(1)
doHash(2)
doHash(3)
doHash(4)

履行成果如下:

  • 每次都是fs先打印
  • 然后是doRequest
  • 最终是4个doHash

【Node】Node是否单线程?实例解说线程池原理

履行进程如下,因为这次咱们线程池中有5个线程,一次最多能一起处理5个使命。刚好咱们有5个使命,每个使命都能分配给一个线程,因而有一个线程能够专门负责处理文件读取。

【Node】Node是否单线程?实例解说线程池原理

假如将线程池巨细改成1

process.env.UV_THREADPOOL_SIZE = 1;
const https = require('https')
const crypto = require('crypto')
const fs = require('fs')
const startTime = Date.now();
const doRequest = (num) => {
    https.request('https://www.baidu.com/', res => {
        res.on('data', (data) => {
        })
        res.on('end', () => {
            console.log(`doRequest ${num} 耗时:`, Date.now() - startTime)
        })
    }).end();
}
const doHash = (num) => {
    crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
        console.log(`Hash ${num} 耗时: `, Date.now() - startTime);
    })
}
doRequest(1)
fs.readFile('test.js', 'utf8', () => {
    console.log('FS 耗时:', Date.now() - startTime)
})
doHash(1)
doHash(2)
doHash(3)
doHash(4)

履行成果如下:

  • 每次都是doRequest先输出
  • 其次是4个dohash
  • FS每次都是最终输出

为什么会呈现这种成果?能够想一想

【Node】Node是否单线程?实例解说线程池原理

总结

经过本篇文章,咱们学习了下面这些东西:

  • 进程线程的常识
  • 了解了cpu怎么调度线程履行
  • 经过实例解说了Node并不是单线程,事情循环线程(主线程)才是单线程。
  • 经过实例分析Node线程池的根本原理,并能够经过理论核算每个使命的履行耗时
  • 一起咱们学到了Node供给的规范库中有些函数是在事情循环线程中履行的,有些是在线程池中履行的,有些是托付给操作系统底层履行的。