进程
一个应用程序,总是通过操作系统启动的,当操作系统启动一个应用程序时,会给其分配一个进程
一个进程拥有独立的、可伸缩的内存空间,原则上不受其他进程干扰
进程之间是可以通信的,只要两个进程双方遵守一定的协议,比如ipc
CPU在不同的进程之间切换执行
虽然一个应用程序在启动时只有一个进程,但它在运行的过程中,可以开启新的进程,进程之间仍然保持相对独立
如果一个进程是直接由操作系统开启,则它叫做主进程
如果一个进程B是由进程A开启,则A是B的父进程,B是A的子进程,子进程会继承父进程的一些信息,但仍然保持相对独立
// nodejs 中开启子进程
const childProcess = require("child_process"); // 导入内置模块
childProcess.exec(在子进程运行的命令, (err, out, stdErr) => {
// 回调函数中可以获取子进程的标准输出,这种数据交互是通过IPC完成的,nodejs已经帮你完成了处理
// err:开启进程过程中发生的错误
// out: 子进程输出的正常内容
// stdErr: 子进程输出的错误内容
// 子进程发生任何的错误,绝不会影响到父进程,它们的内存空间是完全隔离的
});
// 过去,nodejs没有提供给用户创建线程的接口,只能使用进程的方式
// 过去,nodejs还提供了cluster模块,通过另一种模式来创建进程
// 现在,nodejs已经提供了线程模块,对进程的操作已经很少使用了
线程
操作系统启动一个进程(无论是主进程,还是子进程),都会自动为它分配一个线程,称之为主线程
程序一定在线程上运行!!
主线程在运行的过程中,可以创建多个线程,这些线程称之为子线程
当操作系统命令CPU去执行一个进程时,实际上,是在该进程的多个线程中切换执行
线程和进程很相似,它们都是独立运行,最大的区别在于:线程的内存空间没有隔离,共享进程的内存空间,线程之间的数据不用遵守任何协议,可以随意使用
什么时候要使用线程?
使用线程的主要目的,是为了充分使用多核cpu。线程执行过程中,尽量的不要阻塞。
最理想的线程效果:
- 线程数等于cpu的核数
- 线程永不阻塞
- 没有io
- 只存在大量运算
- 线程相对独立,几乎不使用共享数据
线程一般处理cpu密集型操作(运算操作),而io密集型操作不适合使用线程,而适合使用异步
为了避免线程执行过程中共享数据产生的麻烦,nodejs使用独特的线程机制来尽力规避:
// 创建子线程的父线程
const { Worker } = require("worker_threads");
const worker = new Worker(线程执行的入口文件, {
workerData: 开启线程时向其传递的数据,
}); // worker是子线程实例
// 通过EventEmitter监听子线程的事件
worker.on("exit", () => {
// 当子线程退出时运行的事件
});
worker.on("message", (msg) => {
// 收到子线程发送的消息时运行的事件
});
worker.postMessage(任意消息); // 父线程向子线程发送任意消息
worker.terminate(); // 退出子线程
const {
isMainThread, // 是否是主线程
parentPort, // 用于与父线程通信的端口
workerData, // 获取线程启动时传递的数据
threadId, // 获取线程的唯一编号
} = require("worker_threads");
parentPort.on("message", (msg) => {
// 当收到父线程发送的消息时,触发的事件
});
parentPort.postMessage(workerData); // 向父线程发送消息