什么是进程和线程?
进程(Process)是程序运行起来之后的一次执行实例,也是操作系统分配和隔离资源的基本单位;线程(Thread)是进程内部的一条执行路径,也是 CPU 调度的基本单位。

两者不是互相替代的关系,而是包含与协作的关系:一个进程至少包含一个线程,也可以包含多个线程;线程负责执行指令,进程则为线程提供地址空间、文件句柄和其他运行资源。
理解进程和线程,可以帮助你判断程序为什么卡顿、多任务为什么能同时进行,以及开发并发程序时为什么既需要隔离,也需要同步。
一句话记忆:进程负责提供独立的运行环境,线程负责在这个环境里执行具体任务。
什么是进程?
进程不是硬盘上的程序文件,而是程序被加载并开始运行后,由操作系统管理的一次执行实例。同一个程序启动两次,通常会产生两个进程。

例如,双击一个视频播放器后,操作系统通常会为它建立:
- 独立的虚拟地址空间,用来存放代码、数据、堆和映射区域;
- 唯一的进程标识符,也就是 PID;
- 文件描述符或句柄、网络连接、权限和环境变量等运行上下文;
- 用于记录进程状态、资源和调度信息的内核数据结构。
可以把进程理解成一家独立运营的公司:它有自己的办公室、资金和设备。两家公司即使经营同一种业务,也不会默认共用账本和办公空间。
什么是线程?
线程是进程内部实际执行指令的最小执行单元。一个进程启动时至少有一个主线程,也可以继续创建工作线程来处理其他任务。

如果进程是公司,线程就是公司里的员工。员工共享办公室和设备,但各自有当前正在处理的工作。对应到操作系统中:
- 同一进程的线程共享代码段、堆、全局变量和打开的文件;
- 每个线程有自己的程序计数器、CPU 寄存器、调用栈和线程局部存储;
- 任意线程都必须属于某个进程,不能脱离进程单独存在。
所以,“线程没有自己的内存”并不严谨。更准确的说法是:线程没有独立的进程地址空间,但保留自己的栈和执行上下文。
为什么程序需要多线程?
多线程的直接价值,是让一个进程里的多个任务可以交替推进,或者在多个 CPU 核心上同时执行,从而避免一个慢任务阻塞整个程序。

以聊天软件为例,它需要同时处理网络收发、界面刷新和语音播放。如果所有工作都挤在一个执行流中,线程等待网络数据时,排在后面的界面更新就无法执行,用户会感觉应用“卡住了”。
引入多线程后,程序可以这样分工:
- 网络线程负责等待和接收消息;
- 界面线程负责绘制窗口并响应点击;
- 音频线程负责解码和播放语音。

当网络线程进入等待状态时,其他线程仍有机会继续运行。需要注意,多线程不保证程序一定更快;它首先改善的是任务组织和响应能力,实际性能还取决于 CPU 核心数、任务类型、同步成本和调度开销。
并发和并行有什么区别?
并发是多个任务在同一段时间内交替推进,并行是多个任务在同一时刻真正同时执行。 两者都可能出现在多线程程序中,但含义不同。

| 对比维度 | 并发(Concurrency) | 并行(Parallelism) |
|---|---|---|
| 核心含义 | 多个任务交替取得进展 | 多个任务在同一时刻执行 |
| 单核 CPU | 可以通过时间片和任务切换实现 | 无法让多个 CPU 指令流真正同时运行 |
| 多核 CPU | 可以发生 | 不同线程可在不同核心上同时执行 |
| 主要目标 | 提高响应能力和资源利用率 | 缩短可拆分计算任务的执行时间 |
| 常见场景 | 网络服务、GUI、异步任务 | 图像处理、科学计算、批量数据处理 |
单核 CPU 上,操作系统会在极短时间内切换可运行线程,让它们看起来像同时运行。多核 CPU 上,调度器才可能把多个线程放到不同核心上真正并行执行。
进程和线程是什么关系?
进程和线程是包含关系:进程提供资源容器,线程使用这些资源执行任务。一个进程可以包含多个线程,但每个线程只属于一个进程。

这组关系有三个直接结果:
- 进程创建后,至少要有一个线程才能执行程序代码;
- 进程内的线程可以共享数据,因此协作速度通常较快;
- 进程退出时,所属线程和地址空间都会被操作系统回收。
反过来,单个线程正常结束不一定导致整个进程退出;只有主流程的约定、未处理的致命错误或特定运行时行为,才可能让其他线程一同终止。
进程和线程的核心区别是什么?
进程和线程最重要的区别集中在四个维度:资源归属、创建与切换开销、故障隔离、通信方式。

| 对比维度 | 进程 | 线程 |
|---|---|---|
| 基本角色 | 资源分配与隔离的基本单位 | 执行和 CPU 调度的基本单位 |
| 地址空间 | 通常拥有独立虚拟地址空间 | 共享所属进程的地址空间 |
| 私有状态 | PID、地址空间、句柄表等 | 寄存器、程序计数器、调用栈等 |
| 创建成本 | 通常较高,需要建立或复制更多运行上下文 | 通常较低,复用进程已有资源 |
| 切换成本 | 通常较高,可能涉及地址空间等上下文变化 | 同进程线程间切换通常较低 |
| 故障影响 | 隔离较强,一个进程崩溃通常不直接破坏其他进程 | 隔离较弱,线程破坏共享内存可能拖垮整个进程 |
| 通信方式 | 管道、套接字、共享内存、消息队列等 IPC | 可直接访问共享变量,也可使用线程安全队列 |
| 数据共享 | 需要显式通信或共享映射 | 方便,但必须处理同步和可见性问题 |
“线程一定比进程快”只是经验性概括,不是绝对规律。现代操作系统可能使用写时复制、轻量进程或高效 IPC;具体成本要由工作负载、运行时和操作系统实现决定。
为什么进程隔离更强,而线程通信更快?
进程之间默认看不到彼此的地址空间,因此一个进程不能直接读取另一个进程的普通变量。它们需要通过进程间通信(IPC)交换数据,例如管道、套接字、消息队列或共享内存。

这种边界增加了通信和序列化成本,却限制了故障范围:一个进程非法访问内存而崩溃时,其他独立进程通常仍能继续运行。
同一进程内的线程共享堆和全局变量,可以直接传递对象或修改状态,通信延迟更低。但便利也带来风险:一个线程写坏共享内存、死锁或触发进程级致命错误,可能影响所有线程。
因此,选择多进程还是多线程,本质上是在隔离性、通信成本和开发复杂度之间取舍。
什么是数据竞争?为什么共享内存需要同步?
数据竞争(Data Race)是指多个线程在缺少正确同步时并发访问同一内存位置,其中至少一个访问是写操作,导致结果取决于不可预测的执行顺序。

假设余额初始为 100 元,线程 A 和线程 B 都执行“余额加 10 元”:
- A 读取余额 100;
- B 也读取余额 100;
- A 计算并写回 110;
- B 根据旧值计算,也写回 110。
两次加款最后只得到 110 元,而不是正确的 120 元。这种现象也常被称为丢失更新,是竞态条件的一种具体结果。
常见解决方式包括互斥锁、读写锁、信号量、原子操作、线程安全队列和不可变数据。它们的共同目标,是明确谁能在什么时刻读写共享状态。锁并非越多越安全:加锁顺序不一致会造成死锁,锁粒度过大也会降低并发能力。
浏览器如何同时使用进程和线程?
现代浏览器通常同时使用多进程和多线程:进程用于隔离不同职责或站点,线程用于完成单个进程内部的渲染、网络、合成等任务。

一个简化的浏览器结构可能包括:
- 浏览器主进程负责窗口、地址栏、权限和进程协调;
- 渲染进程负责网页内容,不同标签页或站点可能位于不同进程;
- GPU、网络或扩展功能可能由单独进程承担;
- 渲染进程内部再由主线程、合成线程、工作线程等共同完成任务。
这种设计让单个网页崩溃时不必关闭整个浏览器,也让绘制、脚本和后台计算能合理分工。不过,“每个标签页固定对应一个进程”并不准确;浏览器会根据站点隔离策略、资源预算和实现方式复用或拆分进程。
文档自动保存为什么适合后台线程?
文档软件可以让界面线程持续响应键盘输入,再把保存任务交给后台线程处理,从而避免磁盘写入阻塞输入体验。

一次简化的自动保存流程如下:
- 界面线程接收输入并更新内存中的文档状态;
- 定时器到期后,后台任务取得一致的内容快照;
- 后台线程把快照写入临时文件或数据库;
- 保存成功后,再安全替换正式版本并更新状态提示。
这里不能让两个线程毫无约束地读写同一份文档数据。应用通常会使用不可变快照、消息队列、版本号或短时间加锁,避免保存到一半的内容和用户最新输入互相覆盖。
线程数量越多,程序就越快吗?
线程数量不是越多越好。线程会消耗栈内存和内核管理资源,过多的可运行线程还会增加上下文切换、缓存失效和锁竞争。

选择线程数量时,可以先判断任务类型:
- CPU 密集型任务:线程数通常应接近可用 CPU 核心数,再通过基准测试微调;更多线程往往只会争抢核心。
- I/O 密集型任务:线程等待网络或磁盘时不占用 CPU,可以容纳更多并发任务,但线程数仍受内存、连接数和调度成本限制。
- 大量长连接任务:事件循环、异步 I/O 或协程通常比“一个连接一个线程”更节省资源。
没有适用于所有程序的固定线程数公式。可靠做法是用吞吐量、延迟、CPU 利用率、上下文切换次数、内存占用和锁等待时间共同判断。
多进程和多线程应该怎么选?
需要强隔离、独立权限或故障边界时优先考虑多进程;需要频繁共享大量内存数据、追求低延迟协作时,可以考虑多线程,但必须设计同步机制。
| 需求 | 更常见的选择 | 原因 |
|---|---|---|
| 隔离不可信插件或网页 | 多进程 | 限制崩溃和越权访问的影响范围 |
| 并行执行独立计算任务 | 进程池或线程池 | 取决于语言运行时和任务是否受全局锁等约束 |
| GUI 保持响应并执行后台工作 | 后台线程或异步任务 | 避免主线程被 I/O 或计算阻塞 |
| 共享大对象并低延迟协作 | 多线程 | 无需频繁序列化和复制数据 |
| 跨机器扩展服务 | 多进程或多服务 | 线程无法跨越单个进程和主机边界 |
实际软件经常混合两者:先用多个进程建立隔离边界,再在每个进程内用线程池处理并发任务。
常见问题
一个进程至少有几个线程?
一个能够执行代码的进程至少有一个线程,通常称为主线程。进程还可以根据任务需要创建更多线程。
一个线程崩溃会导致整个进程崩溃吗?
有可能。线程共享进程地址空间,非法内存访问、未处理的致命信号或运行时错误可能终止整个进程;普通业务异常是否影响其他线程,则取决于语言运行时和异常处理方式。
进程之间完全不能共享内存吗?
不是。进程默认拥有隔离的虚拟地址空间,但可以通过共享内存、内存映射文件等机制显式映射同一片物理内存。共享后同样需要同步,否则也会发生竞态条件。
多线程一定能利用多核 CPU 吗?
不一定。操作系统可以把可运行线程调度到多个核心,但语言运行时限制、锁竞争、任务不可拆分或线程大多在等待 I/O,都可能让多核利用率不高。
协程和线程有什么区别?
线程通常由操作系统内核调度,协程通常由语言运行时或用户态调度。协程切换成本更低,适合大量 I/O 并发,但执行 CPU 密集任务时仍需要线程或进程配合才能利用多个核心。
如何判断程序卡顿是进程问题还是线程问题?
先观察 CPU、内存、线程状态、锁等待和 I/O 延迟。整个系统内存不足更像资源压力;界面线程长时间执行计算或等待 I/O 会造成无响应;多个线程互相等待可能是死锁;结果偶发错误则应排查数据竞争。
总结:进程负责资源隔离,线程负责执行任务
进程是程序的一次运行实例,提供独立地址空间和资源边界;线程是进程内的执行单元,共享大部分进程资源,并由操作系统调度到 CPU 上运行。

记住下面四点,就能抓住进程和线程的核心:
- 资源:进程拥有独立地址空间,线程共享所属进程的大部分资源;
- 开销:进程创建和切换通常更重,同进程线程通常更轻;
- 隔离:进程故障边界更清晰,线程共享内存但风险会互相传递;
- 协作:进程依赖 IPC,线程可直接共享数据但需要同步。
最终结论是:进程用隔离换取可靠性,线程用共享换取协作效率。好的并发设计不是堆更多线程,而是在正确的位置划分资源边界、执行任务和同步规则。

