欢迎光临
我们一直在努力

node 线程与进程

进程和线程的区别

简单记:进程是 “独立的容器”,线程是 “容器里干活的人”,多人共享容器资源,效率更高但也更容易互相影响。

进程:独立可运行的程序,比如微信,留言及,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 实例和内存,但支持两种通信模式:

  • 消息传递(默认):通过复制数据的方式通信(序列化 / 反序列化),安全但有少量性能开销;
  • 共享内存:通过 SharedArrayBuffer 直接共享内存,无数据复制开销,但需要手动处理并发安全。
  • 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} 累加完成`);

    赞(0)
    未经允许不得转载:171主机测评 » node 线程与进程
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址