线程池学习(一) 理解操作系统 进程 线程 协程及上下文切换

目录

最直白的讲解几者关系

上下文切换

进程跟线程之间的关系

核心结论先敲定:

为什么要有进程?既然线程才干活,直接用线程不行吗?


最直白的讲解几者关系

我们把操作系统比作一个公司 一个个进程比作部门 线程就是部门的成员

而成员一会要干a任务一会要干b任务 ,工作的流程方式叫做协程 。

io密集型 就是成员(线程)不忙但是一直在等 要么考虑加协程(工作流)要么考虑加成员(线程) cpu密集型 就是成员(线程)已经忙不过来了 要么考虑加成员(共享数据频繁) 要么考虑加部门(追求任务隔离性)

上下文切换

上下文切换开销本质上就是不同任务(进程 / 线程 / 协程)之间切换执行时产生的系统开销,但不同类型任务的切换开销来源、量级差异极大,这也是协程在高并发场景下性能优势的核心原因。

下面从技术本质、开销构成、不同任务类型的对比三个维度,帮你把这个概念讲透。

一、上下文切换的本质

操作系统的 CPU 核心同一时间只能执行一个任务(进程 / 线程),当需要切换到另一个任务时,必须先保存当前任务的执行状态 ,再恢复目标任务的执行状态,这个过程就是上下文切换,而保存和恢复状态的所有消耗就是上下文开销。

你可以把它类比成:员工 A 正在做报表,突然要切换去做 PPT,需要先把报表的当前进度、打开的文件、思路都记下来(保存上下文),然后打开 PPT 软件、找到对应的项目文件、回忆之前的设计思路(恢复上下文),这个过程消耗的时间和精力就是 "切换开销"。

二、上下文开销的具体构成

上下文切换的开销主要分为两类,内核态切换的开销远大于用户态:

  1. 内核态上下文切换开销(进程 / 线程)

    • 寄存器保存与恢复:CPU 的通用寄存器、程序计数器(PC)、栈指针等核心执行状态需要保存到内核栈,再加载新任务的寄存器状态。
    • 内核数据结构更新:操作系统需要更新任务的状态(如从运行态变为就绪态)、调度队列、内存管理信息(如页表)等。
    • 缓存失效:CPU 的 L1/L2/L3 缓存、TLB(地址转换缓存)是针对当前任务的,切换后缓存命中率大幅下降,需要重新加载数据,这是开销最大的部分之一。
    • 系统调用开销:切换过程需要从用户态进入内核态,再回到用户态,这个模式切换本身就有固定开销。
  2. 用户态上下文切换开销(协程)

    • 仅需要保存和恢复协程的用户态寄存器(如栈指针、程序计数器),无需涉及内核数据结构。
    • 协程的栈通常很小(几 KB 到几十 KB),切换时无需刷新 CPU 缓存和 TLB,缓存命中率几乎不受影响。
    • 完全在用户态完成,不需要进入内核,没有系统调用的开销。

三、进程、线程、协程的上下文开销对比

任务类型 切换触发者 开销量级 主要开销来源 适用场景
进程切换 操作系统内核 最大(几十到几百纳秒) 页表切换、缓存失效、内核数据结构更新 任务完全隔离、稳定性要求高的场景
线程切换 操作系统内核 中等(几到几十纳秒) 寄存器保存恢复、缓存局部性下降 CPU 密集型任务并行、需要共享资源的场景
协程切换 用户态程序 极小(几纳秒以下) 仅用户态寄存器保存恢复 高并发 IO 密集型场景(如百万级连接的服务器)

四、为什么上下文开销对性能影响巨大?

在高并发场景下,频繁的上下文切换会导致 CPU 的大量时间浪费在 "保存 / 恢复状态" 上,而不是真正执行业务逻辑:

  • 比如一个高并发 Web 服务器,如果用多线程处理 10 万个连接,线程切换开销会占 CPU 的 50% 以上,导致业务处理能力大幅下降。
  • 而用协程处理同样的连接,切换开销几乎可以忽略,CPU 能全力处理业务,性能可提升数倍甚至数十倍。

五、C++ 中的实际体现

  • 线程切换 :当你用std::thread创建多个线程,操作系统会负责调度,当线程数超过 CPU 核心数时,就会频繁发生上下文切换,你可以通过top命令(Linux)看到%sys(内核态 CPU 占用)升高,这就是切换开销的体现。
  • 协程切换 :C++20 协程的切换完全由程序控制,比如用co_await让出执行权时,仅保存协程的栈帧和寄存器状态,开销极小,适合实现高并发的 IO 服务。

进程跟线程之间的关系

核心结论先敲定:

进程本身从不干活,它只是一个「资源容器 / 任务外壳」;

真正执行代码、完成具体工作的,只有线程;

线程是进程的「执行单元」,进程必须依赖线程才能运行,抛开线程,进程就是一个空架子,啥也做不了!

一、再用类比,把「进程不干活、线程干所有活」讲死

公司 = 操作系统 部门 = 进程 → 部门本身不产出、不干活!部门的作用是「申请 + 持有」公司的资源:比如办公室、电脑、打印机、项目预算、客户资料。

部门里的员工 = 线程员工才是真正干活的人!员工拿着部门的资源(用电脑、查资料、用打印机),完成具体的工作(写代码、做报表、谈客户)。

核心对应:

  • 进程(部门):只占有资源,不执行任务;
  • 线程(员工):使用进程的资源,执行具体任务;
  • 没有员工的部门(无线程的进程):空有资源,完全闲置,啥活干不了;
  • 员工必须隶属于某个部门(线程必须属于某个进程):不可能存在脱离部门的员工,也不存在脱离进程的线程。

二、从技术层面,讲清「进程的本质是容器,线程的本质是执行者」

进程的核心使命:「占资源」,仅此而已

操作系统创建一个进程,本质上是为它分配一套独立的专属资源 ,这些资源是线程干活的「基础保障」,进程的所有资源,会被自己内部的所有线程共享,进程的核心工作只有 2 个:

  1. 向操作系统申请资源:独立的内存空间、文件句柄、网络端口、系统权限等;
  2. 持有并管理这些资源:保证资源只给自己的线程用,进程退出时,统一释放所有资源。

进程从头到尾,不会执行任何一行业务代码,它的存在只是为线程「搭好舞台」。

线程的核心使命:「执行代码」,干完所有活

线程是操作系统中最小的执行单元 ,它没有自己的独立资源(几乎 0 资源),但它可以直接使用所属进程的全部资源 。CPU 在操作系统中调度的核心对象,不是进程,而是线程!操作系统的 CPU 核心,只会给线程分配执行时间,让线程去跑代码:

  • 线程拿到 CPU 时间片 → 执行程序里的一行行代码(加减乘除、读写文件、网络请求);
  • 线程干完自己的任务 → 要么等待下一次调度,要么结束运行;
  • 一个进程里可以有多个线程,这些线程共享进程的资源,协同完成一个复杂任务(比如一个 APP 进程,有「界面渲染线程」「网络请求线程」「数据存储线程」,各司其职)。

三、必须掌握的关键细节:「进程一创建,就自带 1 个线程」

你可能会疑惑:我平时写个单线程程序,只创建了进程,没手动创建线程,为啥程序能正常运行? 这是操作系统的「默认规则」:当你创建一个进程时,操作系统会自动为这个进程创建「第一个线程」------ 主线程(Main Thread)

举个最常见的例子:你写一个 C/C++/Java/Python 的简单程序,编译运行后,操作系统会生成一个进程,同时自动创建主线程 ,你的main()函数,本质上就是主线程的入口代码 ,是主线程在执行main()里的所有逻辑。

  • 你不手动创建线程 → 进程里只有「主线程」1 个执行者,串行干活;
  • 你手动创建线程 → 进程里有多个执行者,并行干活;
  • 哪怕你创建 100 个线程,这些线程也都属于同一个进程,共享进程的所有资源。

四、进程 线程 最核心的 3 个关系总结

1. 从属关系:线程是进程的「子集」

线程必须隶属于某一个进程 ,一个进程可以包含1 个或多个线程(主流系统中,进程至少有 1 个线程);反之,不存在脱离进程的独立线程。

2. 分工关系:进程管「资源」,线程管「执行」
  • 进程:资源分配的基本单位 → 只拿资源、不干活;
  • 线程:程序执行的基本单位 → 只用资源、干所有活。
3. 依赖关系:进程的运行,完全依赖线程

进程的生命周期和线程绑定:

  • 进程启动 → 主线程自动启动,开始执行代码;
  • 进程内所有线程都执行完毕 → 进程才会随之退出;
  • 若进程被强制终止 → 它内部的所有线程会被「一刀切」全部销毁。

为什么要有进程?既然线程才干活,直接用线程不行吗?

核心原因:进程是为了「隔离资源」,线程是为了「高效执行」

  • 如果只有线程、没有进程:所有线程共享操作系统的全局资源,一个线程出错(比如内存越界),会直接导致整个操作系统崩溃,安全性极差;
  • 有了进程之后:每个进程的资源是相互独立、相互隔离的,A 进程里的线程出错,只会导致 A 进程崩溃,不会影响 B 进程、更不会影响操作系统,稳定性和安全性直接拉满。

一句话总结两者的存在意义:进程负责「隔离风险、管理资源」,线程负责「高效干活、执行任务」,二者缺一不可,协同工作


最终核心结论:

  1. 进程不干活,只是「资源容器」;
  2. 线程是唯一的「执行者」,干所有活;
  3. 线程属于进程,进程依赖线程才能运行;
  4. 进程管资源,线程管执行。
相关推荐
西岸行者6 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意6 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码6 天前
嵌入式学习路线
学习
毛小茛6 天前
计算机系统概论——校验码
学习
babe小鑫6 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms6 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下6 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。6 天前
2026.2.25监控学习
学习
im_AMBER6 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J6 天前
从“Hello World“ 开始 C++
c语言·c++·学习