【编程技术】进程、线程、协程介绍

文章目录

    • 1. 进程
    • 2. 线程
    • 3. 协程
    • 对比总结表
    • 总结

1. 进程

  • 定义: 进程是程序的一次执行过程,是操作系统进行资源分配和调度的基本单位。 当一个程序被加载到内存中并开始执行时,它就变成了一个进程。
  • 核心特性:
    • 独立性: 每个进程都拥有自己独立的地址空间(内存空间)数据段堆栈文件描述符环境变量程序计数器等。一个进程崩溃通常不会直接影响其他进程(除非通过特定机制通信)。
    • 资源拥有者: 进程是系统资源(CPU时间、内存、I/O设备等)分配的基本单位。操作系统负责为其分配和回收资源。
    • 开销大: 创建、切换和销毁进程需要操作系统进行大量的工作(分配/回收内存、设置页表、保存/恢复CPU状态等),上下文切换成本很高
    • 通信复杂: 进程间通信需要特定的机制,如管道、消息队列、共享内存、信号量、套接字等,因为这些独立的地址空间不能直接访问彼此的内存。
  • 优点:
    • 稳定性高,一个进程崩溃不影响其他进程。
    • 安全性好,内存隔离性强。
  • 缺点:
    • 创建、销毁、切换开销大。
    • 进程间通信复杂且相对较慢。
  • 类比: 想象一个工厂(操作系统)。每个独立的车间就是一个进程。每个车间有自己的原材料仓库(内存空间)、工人(CPU执行流)、生产线(代码执行路径)和独立的出入口规则(资源管理)。车间之间要协作,需要通过专门的物流通道(IPC机制)传递货物(数据)。
  • 应用场景: 运行独立的应用程序(如浏览器、文字处理器、音乐播放器等);需要高隔离性的任务。

2. 线程

  • 定义: 线程是进程内的一个执行流,是CPU调度和执行的最小单位。 一个进程可以包含多个线程。
  • 核心特性:
    • 共享性: 同一个进程内的所有线程共享该进程的地址空间和大部分资源(如全局变量、堆内存、打开的文件描述符等)。每个线程拥有自己独立的栈空间程序计数器
    • 轻量级: 线程是进程内部的实体,创建、切换和销毁线程的开销远小于进程。这是因为它们共享了进程的大部分资源,操作系统需要保存和恢复的状态信息更少(主要是栈指针、程序计数器、寄存器等)。
    • 协作性: 由于共享内存,线程间通信非常方便(直接读写共享变量即可)。但也因此引入了同步问题(竞态条件、死锁),必须使用锁、信号量、条件变量等机制来协调对共享资源的访问。
    • 依赖性强: 一个线程的崩溃(如访问非法内存)通常会导致整个进程崩溃,因为它破坏了共享的地址空间。
  • 优点:
    • 创建、销毁、切换开销小,响应更快
    • 线程间通信(共享内存)非常高效。
    • 能充分利用多核CPU进行真正的并行计算(在拥有多个CPU核心的机器上)。
    • 适合需要频繁交互和共享数据的并发任务。
  • 缺点:
    • 编程复杂,需要仔细处理同步和共享数据问题(易出错)。
    • 一个线程出问题可能影响整个进程。
    • 线程数量不是无限可扩展的(受限于内存、内核调度器开销等)。
  • 类比: 还是那个工厂(进程)。一个车间(进程)里有多条并行的生产线(线程)。这些生产线共享车间的原材料仓库(共享内存)、水电(系统资源)和主出入口(文件描述符等)。每条生产线有自己的工人操作台(栈)和当前工作指令(程序计数器)。生产线之间传递半成品(数据)非常方便,但需要协调避免争抢同一个工具(同步)。
  • 应用场景: Web服务器处理并发请求;图形用户界面程序(GUI线程和后台计算线程);需要利用多核性能的计算密集型任务(如视频编码、科学计算)。

3. 协程

  • 定义: 协程是一种比线程更加轻量级的用户态执行单元,也称为“微线程”或“纤程”。 协程的调度完全由程序员在用户空间控制(或由语言的运行时库管理),不依赖于操作系统的内核线程调度器
  • 核心特性:
    • 用户态调度: 协程的创建、切换和销毁发生在用户态不涉及操作系统内核的上下文切换。这使得协程切换的开销极低(通常只是保存/恢复少量寄存器)。
    • 协作式调度: 协程是协作式多任务的。一个协程必须主动让出执行权(yield)给其他协程,而不是像线程那样由操作系统强制抢占。这避免了锁的需求(在单线程调度器下),但也要求开发者设计好让出点,避免一个协程长时间独占。
    • 极高的并发性: 由于极低的创建和切换开销,一个程序可以轻松创建数万甚至数十万个协程。
    • 通常绑定于线程: 多个协程通常运行在一个或少量几个操作系统线程上。一个线程内的多个协程是串行执行的(除非该线程绑定了多个CPU核心?不,线程在单核上也是并发而非并行,多核才是并行)。
    • 状态保持: 协程在让出时能保存当前的执行状态(局部变量、执行位置),下次恢复时能从中断处继续执行。
  • 优点:
    • 极低的创建、切换开销,超高并发能力。
    • 用户态调度,效率高,减少内核上下文切换负担。
    • 在单线程内通过协作式调度实现并发,避免了多线程的复杂同步问题(在单调度线程环境下,共享数据访问是顺序的,天然无锁)。
    • 异步代码同步写:利用 async/await 等语法,可以写出结构清晰、类似同步风格的异步代码,解决“回调地狱”。
  • 缺点:
    • 不能利用多核CPU进行真正的并行计算(一个线程上的协程是串行的)。需要结合多线程才能利用多核。
    • 需要开发者显式安排让出点(yield),或者依赖语言/库的调度器。一个协程如果不让出会阻塞整个线程。
    • 调试可能更复杂。
  • 类比: 想象车间(进程)里的一条生产线(线程)。在这条生产线上,有一个非常熟练的工人(调度器)。这个工人可以在不同的生产任务(协程)之间极其快速地切换。他记住每个任务做到哪一步了(保存状态),当一个任务需要等待(如等胶水干)时,他主动放下这个任务(yield),马上去做另一个准备好的任务。切换速度非常快,看起来像是多个任务在同时推进。工人(线程)只有一个,但他在高效地管理着多个任务(协程)
  • 应用场景: 高并发网络服务(如Web API服务器,处理大量I/O密集型请求);需要大量轻量级并发执行单元的场景;编写易于理解的异步代码(如爬虫、游戏逻辑);Go语言的goroutine, Pythonasyncio, Kotlincoroutines 等都是协程的流行实现。

对比总结表

特性 进程 线程 协程
定义 资源分配的基本单位,程序的一次执行实例 CPU调度的基本单位,进程内的一个执行流 用户态轻量级执行单元,协作式任务
资源 拥有独立的地址空间和系统资源 共享进程的地址空间和资源,拥有独立栈和寄存器 共享线程的资源,拥有独立栈和上下文
切换开销 很高(内核态切换,涉及内存、页表等) 中等(内核态切换,保存恢复寄存器等) 极低(用户态切换,仅保存恢复少量寄存器)
创建/销毁 开销大 开销比进程小,但比协程大 开销极小
通信 复杂(IPC机制:管道、共享内存、Socket等) 简单(共享内存,但需同步) 简单(共享内存,在单调度线程下无需同步)
同步 通常不需要(天然隔离) 必需(锁、信号量等防止共享资源冲突) 协作式(单线程下无需锁,但需主动让出)
健壮性 高(一个进程崩溃不影响其他进程) 低(一个线程崩溃通常导致整个进程崩溃) 中等(一个协程异常可能影响同线程其他协程)
调度者 操作系统内核 操作系统内核 用户程序或语言的运行时库(用户态调度)
并行性 可在多核上并行 可在多核上并行 本身不行(单线程内并发),需结合多线程
数量级 数十到数百 数百到数千 数万到数十万甚至百万
典型应用 运行独立应用(浏览器、Office) 利用多核(计算密集型)、GUI后台任务、Web服务器 高并发I/O(网络服务)、异步编程、轻量级任务
编程模型 多进程 多线程 异步/协程(async/await
代表实现 fork() (Unix), CreateProcess() (Win) pthreads (POSIX), Thread (Java/.NET) goroutine (Go), asyncio (Python), coroutines (Kotlin)

总结

  • 进程:重量级,资源隔离好,通信开销大,稳定性高。适合运行独立应用。
  • 线程:轻量于进程,共享内存通信高效,但需同步,能利用多核并行。适合需要高效共享数据和利用多核的任务。是多核并行计算的主力。
  • 协程:极度轻量,用户态调度开销极小,超高并发,简化异步编程。最适合处理大量I/O等待型任务(如网络请求、文件读写),通过让出CPU避免阻塞,最大化利用单线程性能。常与线程池结合利用多核。

你可能感兴趣的:(编程技术,开发语言)