大部分操作系统(如Windows、Linux)的任务调度是采用时间片轮转的抢占式调度方式,也就是说一个任务执行一小段时间后强制暂停去执行下一个任务,每个任务轮流执行。任务执行的一小段时间叫做时间片,任务正在执行时的状态叫运行状态,任务执行一段时间后强制暂停去执行下一个任务,被暂停的任务就处于就绪状态等待下一个属于它的时间片的到来。这样每个任务都能得到执行,由于CPU的执行效率非常高,时间片非常短,在各个任务之间快速地切换,给人的感觉就是多个任务在“同时进行”,这也就是我们所说的并发(一个时间间隔内,多个任务同时进行).
进程
CPU是计算机的核心,承担所有计算任务;
操作系统是计算机管理者,负责任务的调度、资源的分配和管理,统领整个计算机硬件;
应用程序侧是具有某种功能的程序,程序是运行于操作系统之上的。
进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。
进程是一种抽象的概念,从来没有统一的标准定义。进程一般由程序、数据集合和进程控制块三部分组成。
程序用于描述进程要完成的功能,是控制进程执行的指令集;
数据集合是程序在执行时所需要的数据和工作区;
程序控制块(Program Control Block,简称PCB),包含进程的描述信息和控制信息,是进程存在的唯一标志。
进程具有的特征:
动态性:进程是程序的一次执行过程,是临时的,有生命期的,是动态产生,动态消亡的;
并发性:任何进程都可以同其他进程一起并发执行;
独立性:进程是系统进行资源分配和调度的一个独立单位;
结构性:进程由程序、数据和进程控制块三部分组成。
线程
早期的操作系统中并没有线程的概念,进程是能拥有资源和独立运行的最小单位,也是程序执行的最小单位。任务调度采用的是时间片轮转的抢占式调度方式,而进程是任务调度的最小单位,每个进程有各自独立的一块内存,使得各个进程之间内存地址相互隔离。
后来,随着计算机的发展,对CPU的要求越来越高,进程之间的切换开销较大,已经无法满足越来越复杂的程序的要求了。于是就发明了线程,线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位.一个进程可以有一个或多个线程,各个线程之间共享程序的内存空间(也就是所在进程的内存空间)。一个标准的线程由线程ID、当前指令指针(PC)、寄存器和堆栈组成。而进程由内存空间(代码、数据、进程空间、打开的文件)和一个或多个线程组成。
进程与线程的区别
1.线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;
2.一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线;
3.进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)及一些进程级的资源(如打开文件和信号),某进程内的线程在其它进程不可见;
4.调度和切换:线程上下文切换比进程上下文切换要快得多。
总之,线程和进程都是一种抽象的概念,线程是一种比进程更小的抽象,线程和进程都可用于实现并发。
单个任务理解
先思考一个问题:
假设有2个cpu,1个任务,请问这一个任务是利用两个cpu执行还是一个cpu呢?
一句话回答:
取决于任务的设计:
如果任务是单线程的 → 只用 1个CPU 执行。
如果任务是多线程/多进程的 → 可以同时用 2个CPU 执行。
补充解释:注意,这里的“任务”可大可小,打开某个网页是一个任务,下载“10”个文件是一个任务,复制一个文件也是一个任务...
无论什么样的任务,只要他的设计是单进程并且是单线程的,那么该任务就只能利用一个CPU单元,一步一步去执行(例如下载10个文件,只能一个个下载,即使其他CPU闲置也不会被利用)
通俗解释:
单线程任务(例如一个简单的计算器程序):
像“一个人干活”,操作系统会随便选一个CPU让它运行,另一个CPU闲着。
总速度 = 单个CPU的速度,和CPU数量无关。
多线程/多进程任务(例如视频渲染软件):
像“两个人分工合作”,操作系统会把不同线程/进程分配到不同CPU上并行执行。
总速度 ≈ 两个CPU的叠加速度(理想情况下快一倍)。
只要有空闲CPU,系统会尽量让任务使用多个CPU(但任务本身必须支持多线程/多进程)。
举个栗子🌰:
任务:计算 1+2+3+…+1000000000。
单线程版:1个CPU吭哧吭哧算 → 耗时10秒。
多线程版(拆成两段):2个CPU同时算 → 耗时约5秒。
总结:
硬件资源(CPU数量)是基础,但能否利用多核取决于软件设计。想让任务跑满多个CPU?记住两个条件:
任务需要支持多线程/多进程(程序员写代码时拆分)。
操作系统和硬件没有瓶颈(比如内存带宽、散热等)。
多任务
多任务的两种表现形式
并发
在一段时间内交替执行多个任务;
例如:在单核cpu处理多任务,操作系统轮流让各个任务交替执行。
并行
在一段时间内真正同时执行多个任务;
例如:对于多核cpu处理多任务,操作系统会给cpu的每个内核安排一个执行的任务,多个内核是真正的一起同时执行多个任务。这里需要注意多核cpu是并行的执行多任务,始终有多个任务一起执行。
因此,多任务是可以提高CPU的利用率的。
多进程和多线程简单理解
多进程介绍
多进程作用
思考:图中是一个非常简单的程序,一旦运行hello.py这个程序,按照代码的执行顺序,func a函数执行完毕后才能执行func b函数.如果可以让func a和func b同时运行,显然执行hello.py这个程序的效率会大大提升.
理解多线程是如何提高程序执行效率的
1. 多线程的核心优势:资源共享与轻量协作
进程 vs 线程的资源分配:
进程:启动时分配独立的内存、文件句柄等资源,资源隔离性强,但创建和切换成本高。
线程:共享同一进程的内存和资源,创建和切换成本极低(仅需分配栈和寄存器)。
关键点:多线程的“资源固定”仅指进程级别的资源总量,但线程通过共享资源避免了重复分配,从而提升效率。
2. 多线程如何提升效率?
**(1) 利用 CPU 空闲时间(I/O 等待与计算重叠)
单线程问题:如果程序需要频繁等待 I/O(如读写文件、网络请求),CPU 会空闲。
多线程解决:
线程 A 等待 I/O 时,操作系统可调度线程 B 使用 CPU。
例如:一个下载工具用多线程同时下载多个文件,等待网络时切换线程,CPU 利用率接近 100%。
**(2) 多核并行计算(CPU 密集型任务)
单线程限制:单线程只能利用一个 CPU 核心。
多线程解决:
将任务拆分为多个子任务,每个线程运行在一个核心上。
例如:视频编码软件用多线程并行处理不同帧,速度显著提升。
**(3) 减少任务响应延迟(实时性提升)
单线程问题:一个耗时操作会阻塞整个程序(如 GUI 界面卡死)。
多线程解决:
后台线程处理耗时任务,主线程保持界面响应。
例如:浏览器用多线程渲染页面时,用户仍可滚动或点击。
3. 多线程一定更快吗?
单线程 vs 多线程的对比场景
场景单线程耗时多线程耗时原因
纯 CPU 计算(单核)
10秒
10秒
无并行,线程切换反而增加开销
CPU + I/O 混合(单核)
15秒
8秒
I/O 等待时切换线程,CPU 利用率提升
纯 CPU 计算(4核)
40秒
10秒
4 线程并行处理,理论加速比 4 倍
4. 多线程的代价与注意事项
线程切换开销:频繁切换线程可能浪费 CPU 周期(需权衡线程数量)。
同步问题:共享资源需加锁(如互斥锁),不当使用会导致死锁或性能下降。
适用场景:
适合:I/O 密集型、可并行计算的任务。
不适合:单核纯 CPU 密集型任务(可能更慢)。
5. 现代操作系统的底层优化
超线程技术:单个物理核心模拟多个逻辑核心,进一步提升并行度。
线程池:复用已创建的线程,减少频繁创建/销毁的开销。
异步 I/O:结合多线程,进一步减少等待时间(如 Node.js 的 libuv 库)。
总结
多线程的核心优势在于:
共享资源:避免重复分配,协作更轻量。
重叠计算与 I/O:减少 CPU 空闲时间。
多核并行:物理层面加速。
实时响应:提升用户体验。
关键公式:总时间 ≈ (任务时间 / 核数) + 线程切换开销 + 同步延迟
合理使用多线程,能显著逼近这一理论最优值!
1. 多线程是否依赖多核?
否。多线程的核心目的是并发(Concurrency),而非绝对意义上的并行(Parallelism)。
单核 CPU 中的多线程:
操作系统通过时间片轮转调度,让多个线程交替执行(看似“同时”运行)。
例如:单核 CPU 可以同时运行浏览器的 UI 线程和下载线程,用户不会感到卡顿。
多核 CPU 中的多线程:
线程可以分配到不同核心上真正并行执行,加速计算密集型任务。
例如:视频渲染软件使用多线程并行处理不同帧,显著缩短时间。
2. 单核环境下多线程的价值
适用场景:
I/O 密集型任务:线程 A 等待磁盘或网络 I/O 时,线程 B 可以继续使用 CPU。
例如:下载文件时,后台线程处理下载,主线程保持界面响应。
提高响应性:避免单线程阻塞导致程序“假死”。
例如:GUI 程序用多线程分离界面渲染和后台计算。
不适用场景:纯 CPU 密集型任务(无 I/O 等待):线程切换会增加开销,单线程可能更快。
3. 为何多线程切换可能“更慢”?
线程切换成本:操作系统需要保存当前线程的上下文(寄存器、栈指针等),加载新线程的上下文。
每次切换耗时约几微秒到几十微秒(具体取决于硬件和操作系统)。
资源争用:多个线程共享进程资源(如内存、文件句柄),需通过锁机制同步,可能导致阻塞和额外开销。
4. 多线程设计的核心思想
并发 vs 并行:
并发:逻辑上的“同时执行”(单核即可实现)。
并行:物理上的同时执行(需多核或多 CPU)。
目标:
最大化资源利用率:让 CPU 尽可能少空闲(尤其是存在 I/O 等待时)。
解耦任务逻辑:将不同职责分配到不同线程(如界面、计算、通信)。
5. 现代操作系统对单核多线程的优化
优先级调度:操作系统优先调度高优先级线程(如 UI 线程),减少用户感知的延迟。
异步 I/O 模型:结合多线程(如事件驱动 + 线程池),减少线程切换频率。
例如:Node.js 用单线程事件循环 + 后台线程池处理 I/O。
总结
纯 CPU 密集型任务 + 单核:多线程可能因切换开销而更慢。
设计不当的多线程程序:锁竞争、过度切换会导致性能下降。
但多线程的价值远超单纯的“加速”,其核心在于:
提升资源利用率(尤其是存在 I/O 的场景)。
改善用户体验(避免程序卡顿)。
为多核环境预留扩展性(单核程序可无缝适配多核硬件)。
最终建议:
对于 I/O 密集型或需要高响应性的任务,即使单核也应使用多线程。
对于纯 CPU 密集型任务,单核环境下优先使用单线程,多核环境下使用多线程并行。
什么是重叠计算?
重叠计算(Overlapping Computation) 的核心思想是:让 CPU 在等待某个操作(如 I/O、网络请求)完成时,不闲着,去执行其他计算任务。本质是 “时间复用”——通过合理安排任务顺序,把等待时间和计算时间重叠起来,从而压缩总耗时。
举个栗子🌰:煮意大利面
假设你要做一盘意面,步骤如下:
烧水(10分钟)
切番茄和洋葱(5分钟)
煮面(水开后煮8分钟)
炒酱(5分钟)
方法1:单线程(一步步做)
总耗时 = 10(烧水) + 5(切菜) + 8(煮面) + 5(炒酱) = 28分钟
问题:烧水时你干等着,浪费了10分钟!
方法2:重叠计算(多线程)
烧水(10分钟) 的同时:
前5分钟:切菜(烧水前5分钟你同步切菜)。
后5分钟:烧水还没好,你闲着(但此时已经切完菜了)。
水烧开后:
煮面(8分钟) 的同时,炒酱(5分钟)。
总耗时 = 10(烧水+切菜) + max(8煮面, 5炒酱) = 10 + 8 = 18分钟
关键点:烧水和切菜重叠,煮面和炒酱重叠!
计算机中的实际场景
场景1:文件下载+处理
单线程:先下载完整个文件(等待),再处理数据(计算)。
总耗时 = 下载时间 + 处理时间。
重叠计算:下载一部分数据 → 立即处理这部分 → 同时下载下一部分。
总耗时 ≈ max(下载时间, 处理时间)(理想情况下)。
场景2:游戏渲染
单线程:先计算物理碰撞(计算),再渲染画面(GPU 等待)。
重叠计算:
线程A计算下一帧的物理碰撞(CPU)。
线程B渲染当前帧的画面(GPU)。
GPU渲染时,CPU不闲着,直接准备下一帧数据。
重叠计算的实现方式
多线程:一个线程等待 I/O,另一个线程执行计算。
异步编程:单线程内通过事件循环调度,先发起 I/O 请求,在等待期间执行其他代码。
硬件加速:用 GPU/DMA 等专用硬件处理特定任务,CPU 并行做其他事。
为什么重叠计算能提速?
公式:总耗时 ≈ 最大阶段的耗时(而非各阶段耗时之和)。
核心:消灭“空闲等待”,让 CPU/GPU/磁盘等设备始终满负荷工作。
再举个极端例子:
任务A:计算10秒,无 I/O。
任务B:下载文件10秒,无计算。
单线程:10 + 10 = 20秒。
重叠计算:max(10, 10) = 10秒(速度翻倍)!
总结
重叠计算就像**“一心多用”**:
生活中:等公交时背单词,洗衣机转着时去擦桌子。
编程中:在等待网络返回时预处理数据,在读取磁盘时压缩已加载的内容。记住原则:让所有硬件设备(CPU/GPU/磁盘/网络)尽量同时忙起来,别让它们闲着!
多进程
多进程提升效率的原理和多线程类似,但实现方式不同。一句话总结:
多进程通过 “同时利用多个 CPU 核心” 和 “减少任务间的互相阻塞” 来提速,但进程之间资源独立,切换成本更高,适合强隔离性或多核并行计算的场景。
举个极简例子🌰:
任务:计算 100 个数字的平方(假设 4 核 CPU)。
单进程:1 个进程自己算 100 次 → 耗时 100 秒。
多进程:4 个进程各算 25 次(4 核并行)→ 耗时约 25 秒。
核心逻辑:把任务拆给多个进程,在多核上真正并行执行。
多线程 vs 多进程的核心区别:
多线程多进程
资源
共享内存,协作快
内存独立,安全性高
场景
I/O 等待、界面响应
多核并行、程序隔离
代价
容易死锁,调试难
占用资源多,切换慢
一句话记住:多线程是 “多人共用一间办公室协作”,多进程是 “多个独立办公室同时开工”,目标都是别让 CPU 闲着!
单进程但是多线程程序会使用单核还是多核
一句话回答:
单进程的多线程程序可以使用多核!只要操作系统支持,线程会被自动分配到不同CPU核心上并行执行。但具体能利用多少核心,还取决于线程数量和任务设计。
通俗解释:
单核 CPU:
所有线程轮流在单核上运行(并发,非真正并行),线程切换由操作系统调度。
效果:提高响应性(比如后台下载时界面不卡),但计算速度不会变快。
多核 CPU:
操作系统会将不同线程分配到不同核心上,真正并行执行。
效果:计算密集型任务速度显著提升(如视频转码、科学计算)。
举个栗子🌰:
任务:计算 1亿次加法(假设4核CPU)。
单线程:1个核心跑满,耗时10秒。
4线程:4个核心各算2500万次,耗时约2.5秒。
关键规则:
操作系统决定调度:程序员只需创建线程,操作系统自动分配核心(无需手动指定)。
线程数量建议:理想情况下,线程数 ≈ CPU核心数(过多会因切换开销降低效率)。
共享资源的代价:线程间共享内存时,需用锁/原子操作避免冲突,否则可能引发性能问题。
验证方法:
Windows:打开任务管理器 → 性能标签 → 观察不同CPU核心的利用率。多线程程序运行时,多个核心的利用率应同时上升。
Linux:使用 top 命令按 1 展开核心视图 → 观察各核心负载。
例外情况:
伪多线程:如果所有线程都在等待I/O(如网络请求),可能看起来只用了单核。
锁竞争:线程频繁抢锁时,实际并行度下降(多个核心可能轮流空闲)。
总结:
多核可用时:单进程多线程天然支持多核并行,性能提升直接。
单核环境:多线程仅提升响应性,不加速计算。核心原则:多线程的价值是让所有CPU核心都忙起来!