2. 进程与线程基础

各位同学,今天我们来聊聊并发编程最底层的两个概念——进程和线程。说实话,这两个东西我刚开始学的时候也绕了很久。后来在项目中踩过几次坑,才真正理解它们的区别。

先问大家一个问题:你打开电脑,同时听音乐、写文档、刷网页,操作系统是怎么让这么多程序“同时”运行的?

答案就在进程和线程里。

进程的概念:程序的一次“活”

进程是什么?简单说,就是程序的一次执行过程。你双击一个exe,操作系统就创建一个进程来运行它。

进程有自己的独立空间——内存、文件句柄、寄存器状态,谁也碰不到谁的。我当年做嵌入式开发时,一个进程崩溃了,其他进程纹丝不动。这就是进程隔离的好处。

每个进程包含:

  • 代码段:程序指令
  • 数据段:全局变量、静态变量
  • 堆:动态分配的内存
  • 栈:函数调用、局部变量
  • PCB(进程控制块):操作系统管理进程的数据结构

核心要点:进程是资源分配的最小单位。每个进程都有独立的地址空间,切换开销大。

进程的生命周期:从生到死

进程不是一直运行的。它有几个状态,我习惯用“生老病死”来记:

  1. 创建(New):程序被加载到内存,PCB被创建
  2. 就绪(Ready):万事俱备,只欠CPU
  3. 运行(Running):CPU正在执行指令
  4. 阻塞(Blocked):等I/O、等锁、等信号
  5. 终止(Terminated):执行完毕或被杀死

嗯,这里要注意:就绪和阻塞的区别。就绪是“我能跑,但CPU没空理我”;阻塞是“我跑不了,得等某个事件”。

我曾经在调试一个网络服务时,发现进程一直卡在阻塞状态。查了半天,原来是socket没设置超时。那次之后,我写代码都会检查所有阻塞操作的超时机制。

线程的概念:进程里的“轻量级工人”

线程是什么?它是进程内的一个执行流。一个进程可以有多个线程,共享进程的资源。

说白了,进程是“工厂”,线程是“工人”。工厂(进程)提供场地和设备,工人(线程)在里面干活。

线程共享的东西:

  • 代码段
  • 数据段
  • 堆
  • 打开的文件

线程独有的东西:

  • 栈(每个线程有自己的调用栈)
  • 寄存器状态
  • 线程ID

我的经验:线程切换比进程切换快得多。因为线程共享地址空间,切换时不需要切换页表。但共享也带来了麻烦——数据竞争。我见过太多因为多线程没加锁导致的bug,排查起来特别痛苦。

线程的优势:为什么我们需要它?

你想想看,如果每个任务都开一个进程,那系统开销得多大?线程的优势就在这里:

  • 创建快:线程创建比进程快10-100倍
  • 切换快:上下文切换开销小
  • 通信方便:共享内存,不需要IPC
  • 资源占用少:一个进程开100个线程,比开100个进程省太多

我记得有一次做高并发服务器,用进程模型,每个连接一个进程,结果内存直接爆了。换成线程池模型,同样的机器能支撑10倍以上的连接。

进程与线程的对比:一张表说清楚

对比维度 进程 线程
资源拥有 独立地址空间 共享进程资源
创建开销 大(分配PCB、地址空间) 小(只需分配栈和TCB)
切换开销 大(切换页表、刷新TLB) 小(只需切换寄存器)
通信方式 IPC(管道、消息队列、共享内存) 直接读写共享变量
安全性 高(隔离性好) 低(一个线程崩溃可能影响整个进程)
适用场景 独立任务、安全性要求高 高并发、资源共享

避坑指南:我曾经在一个项目中,为了追求性能,把所有逻辑都塞进一个进程的多个线程里。结果一个线程的bug导致整个进程挂掉,所有用户都受影响。后来我学乖了——关键服务用进程隔离,非关键部分用线程提升效率。

用户态与内核态线程:两种实现方式

线程的实现有两种方式。嗯,这里有点绕,但很重要。

用户态线程(User-Level Threads)

线程的管理完全在用户空间完成,内核根本不知道有线程存在。每个进程自己维护一个线程表。

优点:

  • 切换不需要系统调用,速度极快
  • 可以自定义调度策略
  • 不依赖操作系统

缺点:

  • 一个线程阻塞(比如I/O),整个进程都阻塞
  • 无法利用多核(内核只调度进程)

内核态线程(Kernel-Level Threads)

线程由操作系统内核管理。每个线程在内核中都有对应的TCB(线程控制块)。

优点:

  • 一个线程阻塞,其他线程可以继续运行
  • 可以利用多核CPU
  • 内核负责调度,更公平

缺点:

  • 切换需要系统调用,开销大
  • 创建和销毁成本高

实际应用:现代操作系统(Linux、Windows)都使用内核态线程。但很多语言运行时(如Go的goroutine、Java的虚拟线程)在用户态实现了轻量级线程,结合了两者的优点。我个人觉得,理解这个区别对写出高性能并发代码很有帮助。

知识体系总览

下面这张图,我把本章的核心知识点串起来了。你可以看到进程和线程的关系,以及它们各自的生命周期和实现方式。

进程与线程知识体系 进程 (Process) • 资源分配最小单位 • 独立地址空间 • 切换开销大 • 生命周期:创建→就绪→运行→阻塞→终止 线程 (Thread) • CPU调度最小单位 • 共享进程资源 • 切换开销小 • 每个线程独有栈和寄存器 包含 用户态线程 • 用户空间管理,内核无感知 • 切换快,无需系统调用 • 缺点:阻塞会导致整个进程阻塞 • 代表:Go goroutine、Java虚拟线程 内核态线程 • 内核管理,有TCB • 一个线程阻塞不影响其他线程 • 缺点:切换需要系统调用 • 代表:Linux pthread、Windows线程 进程是资源容器,线程是执行单元。理解这个,并发编程就入门了。

好了,这一章的内容就到这里。进程和线程是并发编程的基石,搞懂它们,后面的内容会轻松很多。

个人建议:学完理论后,最好动手写几个demo。比如用C语言写一个多进程程序和一个多线程程序,对比它们的创建时间和切换开销。实践出真知,这话一点不假。