进程和线程的区别
简单记:进程是 “独立的容器”,线程是 “容器里干活的人”,多人共享容器资源,效率更高但也更容易互相影响。
进程:独立可运行的程序,比如微信,留言及,VSCODE 进程是操作系统资源分配的最小单位(资源包括内存、CPU 时间片、文件句柄等),每个进程都有自己独立的内存空间,进程之间互不干扰。 线程:是进程的执行单位,一个进程可以包含多个县城,比如微信进程中,有接收消息线程,渲染界面线程 线程是调度执行的最小单位 ,同一进程内的线程共享进程的内存和资源。
类比:进程像一家 “独立的公司”,有自己的办公场地(内存)、资金(系统资源);线程像公司里的 “员工”,共享公司的场地和资金,各自做不同的工作,协作完成公司整体任务。
| 资源分配 | 系统资源分配的最小单位 | 资源调度 / 执行的最小单位 |
| 内存空间 | 每个进程有独立的内存空间 | 共享所属进程的内存空间 |
| 通信方式 | 复杂(需 IPC:管道、套接字、共享内存等) | 简单(直接读写进程内共享变量) |
| 创建 / 销毁开销 | 大(需分配独立内存、资源) | 小(仅需栈空间,共享进程资源) |
| 独立性 | 高(进程崩溃不影响其他进程) | 低(线程崩溃可能导致整个进程崩溃) |
| 切换开销 | 大(需切换地址空间、资源上下文) | 小(仅切换执行上下文) |
| 控制权 | 由操作系统内核管理 | 可由进程自己管理(用户级线程)或内核管理 |
1. 进程的使用场景
- 独立运行的程序:比如你写的一个 UniApp 打包后的 APP、一个 Python 脚本单独运行,都是一个进程。
- 需隔离资源的场景:比如服务器上的多个服务(数据库、后端接口、前端静态服务),用不同进程运行,避免一个服务崩溃影响其他服务。
2. 线程的使用场景
- 同一任务的并行处理:比如你之前写的称重代码中,串口数据接收线程、定时读取重量线程,都属于同一个 APP 进程内的线程,共享串口资源和重量数据。
- 耗时操作不阻塞主线程:比如 UniApp 中,网络请求、串口通信都要放在子线程,避免阻塞 UI 渲染线程(主线程),否则界面会卡顿。
进程间的通信
node中进程通讯分为两种:父子进程通信
无亲缘关系进程通信
进程间通信(Inter-Process Communication,IPC)是指在操作系统中,不同进程之间交换信息和数据的过程,常见的进程通信方式包括:
- 内置 IPC 通道 //父子进程
- 管道(Pipe) //无亲缘关系进程通信
- 网络套接字(Socket) //无亲缘关系进程通信
- 文件 / 数据库(Redis 发布订阅) // 无亲缘关系进程通信
| 内置 IPC 通道 | 父子进程(Node.js 间) | 高效、原生、使用简单 | 仅父子进程、不跨机器 |
| 管道 | 父子进程 / 本地进程 | 轻量、系统原生 | 仅本地、单向 / 半双工 |
| TCP Socket | 任意进程(跨机器 / 跨语言) | 通用、跨平台、实时性好 | 有网络开销、需处理连接 |
| Redis 发布订阅 | 跨进程 / 跨机器 | 解耦、易扩展 | 依赖 Redis 服务、有延迟 |
1.父子进程通信
Node.js 的 child_process 模块(尤其是 fork() 方法)内置了高效的 IPC 通道,这是实现父子进程通信最便捷的方式。
父进程
const {fork} = require("child_process");
//创建子进程
var fork1 = fork("./common/ChildIpc");
fork1.on("message", message => {
console.log(`父进程收到子进程消息:`, message);
})
fork1.send({
data: 123
})
setTimeout(()=>{
console.log("123")
},5000)
子进程
process.on("message", msg => {
console.log("收到主进程的消息", msg)
})
另外 cluster 模块, cluster 模块基于 child_process.fork() 实现,专门用于多核 CPU 利用,主进程和工作进程之间同样通过 send()/message 通信。
2.无亲缘关系进程通信
1. 管道(Pipe)
管道是操作系统提供的基础 IPC 机制,分为匿名管道(仅父子进程)和命名管道(任意进程)。Node.js 中 child_process 的 spawn/exec 方法默认会创建管道,用于标准输入 / 输出(stdin/stdout/stderr)通信。
父进程
const {spawn} = require('child_process');
var childProcessWithoutNullStreams = spawn('node', ["./common/ChildIpc.js"]);
childProcessWithoutNullStreams.stdin.write("父进程通过管道发送的数据");
childProcessWithoutNullStreams.stdin.end();
// 父进程监听子进程的 stdout 输出(子进程的回复)
childProcessWithoutNullStreams.stdout.on('data', (data) => {
console.log('父进程收到子进程回复:', data.toString());
});
setTimeout(() => {
console.log("123")
}, 5000)
子进程
process.stdin.on("data", (data) => {
console.log("收到主进程管道的消息", Buffer.from(data).toString());
process.stdout.write("反馈");
})
2.socket
// 进程 A(服务端)
const net = require('net');
const server = net.createServer((socket) => {
console.log('有进程连接成功');
// 接收客户端数据
socket.on('data', (data) => {
console.log('服务端收到:', data.toString());
// 回复客户端
socket.write('服务端已收到消息:' + data.toString());
});
});
server.listen(3000, '127.0.0.1', () => {
console.log('TCP 服务端启动,监听 3000 端口');
});
// 进程 B(客户端)
const net = require('net');
const client = net.connect({ port: 3000, host: '127.0.0.1' }, () => {
console.log('客户端连接成功');
// 发送数据给服务端
client.write('进程 B 发送的消息');
});
// 接收服务端回复
client.on('data', (data) => {
console.log('客户端收到:', data.toString());
client.end();
});
2.共享存储 (redis发布订阅)
// 进程 1(发布者)
const { createClient } = require('redis');
const publisher = createClient(); // 默认连接本地 Redis
publisher.connect();
setInterval(async () => {
const msg = `当前时间:${new Date().toLocaleString()}`;
await publisher.publish('process-channel', msg);
console.log('发布者发送:', msg);
}, 1000);
// 进程 2(订阅者)
const { createClient } = require('redis');
const subscriber = createClient();
subscriber.connect();
subscriber.subscribe('process-channel', (msg) => {
console.log('订阅者收到:', msg);
});
线程间的通信
和进程间通信不同,Node.js 的工作线程虽然有独立的 V8 实例和内存,但支持两种通信模式:
1. 基础方式:主线程 ↔ 工作线程(消息传递)
主线程
const {Worker} = require("worker_threads")
var worker = new Worker("./common/ChildThread.js", {
workerData: {num: 1e9} // 传递给工作线程的数据
});
// 监听工作线程的消息
worker.on('message', (result) => {
console.log('工作线程计算结果:', result);
//关闭子线程
worker.terminate()
});
// 监听工作线程错误
worker.on('error', (err) => {
console.error('工作线程出错:', err);
});
// 监听工作线程退出
worker.on('exit', (code) => {
if (code !== 0) {
console.error(`工作线程退出,码值: ${code}`);
}
});
worker.postMessage({data: {num: 1e9}});
// 主线程继续执行其他逻辑
console.log('主线程不阻塞,继续执行');
子线程
parentPort.on('message', message => {
console.log("收到主线程的消息", message);
parentPort.postMessage("xxxxxxxxxxxxxx");
})
2. 进阶方式:工作线程 ↔ 工作线程(直接通信)
const {Worker} = require("worker_threads")
const thread = require("worker_threads")
var worker = new Worker("./common/ChildThread.js",);
var worker1 = new Worker("./common/ChildThread1.js",);
let {port1, port2} = new thread.MessageChannel();
// 监听工作线程的消息
worker1.on('message', (result) => {
console.log('工作线程计算结果:', result);
//关闭子线程
worker.terminate()
worker1.terminate()
});
const {parentPort} = require("worker_threads")
let worker2;
parentPort.on("message", message => {
worker2 = message.port;
worker2.on('message', (data) => {
console.log(`worker2 收到 worker1 的消息:${data}`);
parentPort.postMessage("dfdf")
});
})
let worker1;
parentPort.on('message', message => {
worker1 = message.port;
worker1.on('message', (data) => {
console.log(`worker2 收到 worker1 的消息:${data}`);
worker1.postMessage('hello worker1,我是 worker2!');
})
// 主动向 worker2 发消息
worker1.postMessage('hello worker2,我是 worker1');
})
3. 高性能方式:共享内存(SharedArrayBuffer)
通过 SharedArrayBuffer 实现线程间内存共享,数据无需复制,适合大数据量传输(如实时计算、数据共享),但需要注意并发安全。
// main.js
const { Worker } = require('worker_threads');
// 创建共享内存(单位:字节,这里分配 4 字节存整数)
const sharedBuffer = new SharedArrayBuffer(4);
// 用 Uint32Array 包装共享内存,方便操作
const sharedArray = new Uint32Array(sharedBuffer);
// 初始化值为 0
sharedArray[0] = 0;
// 创建两个工作线程,共享同一块内存
const worker1 = new Worker('./worker-shared.js', {
workerData: { sharedBuffer, id: 1 }
});
const worker2 = new Worker('./worker-shared.js', {
workerData: { sharedBuffer, id: 2 }
});
// 监听线程退出,打印最终结果
setTimeout(() => {
console.log(`共享内存最终值:${sharedArray[0]}`);
worker1.terminate();
worker2.terminate();
}, 1000);
// worker-shared.js
const { workerData } = require('worker_threads');
const { sharedBuffer, id } = workerData;
// 包装共享内存
const sharedArray = new Uint32Array(sharedBuffer);
// 两个线程同时累加 1000 次(模拟并发)
for (let i = 0; i < 1000; i++) {
// 使用 Atomics 保证原子操作,避免竞态问题
Atomics.add(sharedArray, 0, 1);
}
console.log(`线程 ${id} 累加完成`);




