;六个问题弄懂线程与进程
1.有了进程之后为什么发明线程
进程最开始负责两块功能
- 资源占有:拥有独立的内存空间和I/O资源
- 执行单元:在CPU执行指令
[!CAUTION]
后来人们发现这样太笨重了,如果我需要一个程序内部的功能同时运行(比如一边打字一边自动保存一边代码审查),就需要创建多个进程,但这样做系统开销非常大。
采用任务细分的思想,人们设计了线程,把存储和执行分开。
此后进程就像是一个资源容器,负责提供地址空间、文件、锁等资源。
而线程就负责当轻量级的活动者,只负责在CPU上执行代码。它需要拥有的是保证它能运行的最小资源:一组寄存器、一个栈、一个状态。
[!IMPORTANT]
所以进程像工厂,线程像工人。
这里你是不是可以理解:
- 线程更好的实现了并发
- 线程极大的减小了系统开销
2.进程负责实现代码吗?
不是
[!WARNING]
有个常见误解:进程是正在运行的程序,所以它当然是在执行代码
从第一个问题可以看出,在现代操作系统中:
- 进程是资源拥有的基本单位(管理内存、文件、外设等)。
- 线程是CPU调度和分派的基本单位(真正在CPU上执行指令)。
[!IMPORTANT]
所以一个“正在运行的进程”,本质上是该进程内的至少一个线程正在被CPU执行
3.进程和线程是否有自己独立的内存空间,为什么这么设计?
它们都有内存空间,但是前者是独立,后者是共享。
同一进程内的所有线程共享该进程的整个内存空间(代码段、数据段、堆等)。每个线程只独立拥有:
- 一组寄存器的值(硬件上下文)
- 一个栈(存放局部变量、函数调用信息)
进程是因为需要安全和隔离。相互不干扰、独立崩溃、数据私密。
而进程是因为需要高效的通信和切换。符合轻量化设计理念。
4.进程和线程的崩溃有什么区别?为什么会有这个区别?
一个进程崩溃了,一般不会影响其他进程。
但是一个线程崩溃了,往往会导致它所属的整个进程崩溃。
原因就是第三个问题提到的,进程有自己独立的内存空间,而线程是共享的。
- 进程之间:拥有独立的内存空间。一个进程崩溃后,操作系统回收其占用的资源,其他进程的内存不受任何影响。
- 线程之间:共享所属进程的整个内存空间。当一个线程执行非法操作(如访问野指针、数组越界、除零、栈溢出)时,会破坏进程的地址空间。操作系统检测到该进程的内存被破坏或产生非法访问,为了系统稳定,会终止整个进程。
5.线程被称为“轻量级进程”这个说法对吗?
[!WARNING]
在调度层面成立,在资源层面不成立
准确的方面:
- 线程和进程都是执行流,有程序计数器、寄存器、栈。
- 进程是对程序的“隔离拆分”,线程是对进程的“并发拆分”。
- 线程和进程都有状态(就绪、运行、阻塞等)。
- 线程和进程都是调度实体(虽然现代OS以线程为调度单位)。
**可能造成的误解:
- 资源拥有:进程拥有独立资源;线程不拥有资源,共享进程的资源。
- 独立性:进程之间高度独立;同一进程内的线程紧密耦合,一个崩溃影响整个进程。
- 切换开销:进程切换重,线程切换轻。
- 创建开销:进程创建需要复制/分配资源表、内存空间;线程只需要创建栈和TCB。
6.线程切换比进程切换快,那是否任何时候都该用多线程而不是多进程
[!IMPORTANT]
不是,需要隔离和安全用进程,需要高效协作用线程。
多进程更合适的场景:
- 需要高可靠性和隔离性:如Chrome浏览器每个标签页独立进程,一个崩溃不影响其他。
- 安全性要求高:不同用户、不同权限的任务,进程间隔离防止数据泄露。
- 分布式/多机系统:进程可以跨机器运行,线程不能跨机器。
- 任务独立性高、交互少:如编译多个源文件。
多线程更合适的场景:
- 需要频繁共享数据:如生产者-消费者模型。
- I/O密集且有交互:如GUI程序(一边响应点击,一边后台计算)。
- 需要低开销并发:创建和切换频繁的场景。