进程、线程;并发、并行;异步
1. 进程与线程
-
进程是cpu资源分配的最小单位(是能拥有资源和独立运行的最小单位)
-
线程是cpu调度的最小单位(线程是建立在进程的基础上的一次程序运行单位,一个进程中可以有多个线程)
对比项 | 进程(Process) | 线程(Thread) |
---|---|---|
资源 | 每个进程有独立的地址空间、数据、文件描述符等。 | 同一进程内的线程共享地址空间、文件、全局变量。 |
通信 | 进程间通信(IPC)需要额外机制(管道、消息队列、共享内存等)。 | 线程可以直接共享数据,通信开销低。 |
切换开销 | 进程切换涉及完整的上下文切换,开销大。 | 线程切换开销较小,因为线程共享进程资源。 |
崩溃影响 | 一个进程崩溃不会影响其他进程。 | 一个线程崩溃可能导致整个进程崩溃。 |
并发性 | 进程间并发能力强,适合独立任务。 | 线程间并发能力更强,适合并行计算。 |
适用场景 | 适合独立运行的任务,如浏览器的不同标签页、Docker 容器等。 | 适合需要共享资源的任务,如 Web 服务器、数据库连接池等。 |
2.并发(Concurrency)、并行(Parallelism)
并发:指在同一时间段内处理多个任务,这些任务是交替执行,而不是同时执行。
- 可以通过 时间片轮转 的方式让不同任务轮流执行。
- IO密集型任务常采用并发,例如 Web 服务器处理多个请求、数据库查询
举例:在单核 CPU 上,操作系统快速切换执行任务,比如每个任务执行 10ms,然后切换到下一个任务,任务之间看起来是并行执行的,实际上是交替执行。
并行:指的是同时执行多个任务,多个任务在不同的处理器核心上同时执行,真正意义上是同时进行,并行需要多核处理器支持。
- 任务是独立的,通常每个任务需要完全独立的资源。
- 通过多核 CPU 或 多台机器实现,常见于 CPU 密集型 的任务,例如科学计算、视频编码。
举例:在多核处理器中,你可以将任务分配给不同的核心,核心 1 执行任务 A,核心 2 执行任务 B,两者同时执行,相互不干扰。
3.异步
异步编程是一种控制流的方式,当一个任务执行时,不需要等待它完成,而是继续执行后续的代码,直到任务完成时,通知回调或返回结果。
-
特点:
- 异步编程通常基于 事件循环(Event Loop)机制,主线程可以继续处理其他任务,而不阻塞等待某个任务的完成。
- 常见于 I/O 密集型 操作,如文件读取、数据库查询、网络请求等。
- 回调函数、Promise 和 async/await 是实现异步编程的常用方式。
-
举个例子:
- 在 Node.js 中,读取文件时不会阻塞主线程,而是使用异步的
fs.readFile()
方法,当文件读取完成时,回调函数会被调用。
- 在 Node.js 中,读取文件时不会阻塞主线程,而是使用异步的
4.js异步遍历数组(串行、并行)
串行遍历指的是一个任务必须等待前一个任务完成之后才能开始执行。我们可以使用 async/await
来实现串行遍历。
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); const asyncSerialTraverse = async (arr) => { for (let i = 0; i < arr.length; i++) { console.log(`开始处理: ${arr[i]}`); await delay(1000); // 模拟异步操作 console.log(`完成处理: ${arr[i]}`); } }; const arr = [1, 2, 3, 4]; asyncSerialTraverse(arr);
并行遍历指的是同时启动所有的异步任务,而不是等待每个任务完成再执行下一个任务。使用 Promise.all()
使得所有的异步任务几乎同时启动,但它并不意味着每个任务都在完全独立的线程中运行。
const asyncParallelTraverse = async (arr) => { const promises = arr.map(async (item) => { console.log(`开始处理: ${item}`); await delay(1000); // 模拟异步操作 console.log(`完成处理: ${item}`); }); await Promise.all(promises); // 等待所有的异步操作完成 }; const arr = [1, 2, 3, 4]; asyncParallelTraverse(arr);
5.node child_process
node遵循的是单线程单进程的模式,node的单线程是指node中只有一个js引擎实例在主线程上运行,node的单线程模式,只维持一个主线程,大大减少了线程间切换的开销。
child_process
模块用于创建子进程。
- Node.js 通过
child_process
模块创建子进程,每个子进程都有独立的 V8 实例和事件循环,不会与主进程共享内存。 - 进程之间通过 IPC(进程间通信) 进行通信,主要方式是 标准输入/输出(stdin、stdout) 和 消息传递(message)。
- 子进程可以执行 shell 命令 或者 Node.js 脚本,甚至可以派生更多子进程。
Node.js 提供了 4 种创建子进程的方法:
方法 | 是否创建 Shell | 是否支持 IPC(进程通信) | 适用场景 |
---|---|---|---|
exec | ✅ 是 | ❌ 否 | 适用于执行**简单 Shell 命令**,如 `ls`、`pwd`。 适合小输出任务,数据缓存在缓冲区。 |
execFile | ❌ 否 | ❌ 否 | 适用于执行**二进制文件**或**可执行文件**(如 `.exe`、`.sh`、`python`)。 比 `exec` 更安全高效。 |
spawn | ❌ 否 | ❌ 否 | 适用于**长时间运行的进程**(如 `ping`、日志监控)。 支持**流式通信**,可以实时读取 `stdout`。 |
fork | ❌ 否 | ✅ 是 | 适用于**创建 Node.js 子进程**,支持进程间通信(`process.send()`)。 适合**CPU 密集型计算**,如大数据处理。 |