进程、线程和协程

70 阅读5分钟

1. 进程、线程和协程

1.1 进程

具体来说,进程的内存占用主要由以下几部分组成:

  1. 代码段(Code Segment):包括程序的指令、常量和只读数据等,通常大小为几 MB 到几十 MB 不等。
  2. 数据段(Data Segment):包括程序的静态变量和全局变量等可读写的数据,通常大小也为几 MB 到几十 MB 不等。
  3. 堆(Heap):动态分配的内存空间,用于存放程序运行期间申请的内存块,通常大小为几 MB 到几百 MB 不等。
  4. 栈(Stack):用于存放程序调用栈和局部变量等信息,通常大小为几 MB 到几十 MB 不等。
  5. 其他内存映射区域(Memory Mapped File):用于映射文件和设备等资源的内存空间,通常大小与映射的文件和设备大小相关。

1.2 线程

具体来说,线程的内存占用主要由以下几部分组成:

  1. 线程控制块(Thread Control Block, TCB):包括线程状态、线程上下文、堆栈指针等信息,通常大小为几 KB 到几十 KB 不等。
  2. 线程堆栈(Thread Stack):用于存放线程函数的调用栈和局部变量等信息,通常大小为几百 KB 到几 MB 不等。
  3. 线程相关的数据结构和资源:如线程私有变量、锁、条件变量等等,其大小与具体使用方式和应用场景相关。 在常见的协程库中,协程的内存占用主要由以下几部分组成:

1.3 协程

  1. 协程控制块(Coroutine Control Block, CCB):包括协程状态、协程上下文、栈指针等信息,通常大小为几十字节到几百字节不等。
  2. 协程堆栈(Coroutine Stack):用于存放协程函数的调用栈和局部变量等信息,通常大小为几 KB 到几十 KB 不等。
  3. 协程相关的数据结构和资源:如协程私有变量、锁、条件变量等等,其大小与具体使用方式和应用场景相关。

在操作系统中,进程是操作系统资源分配的单位;线程是处理器调度和执行的基本单位。

在 Linux 中,线程和进程都是通过系统调用来创建和管理的。线程通常是通过 pthread 库来创建和管理的,而进程则是通过 fork 系统调用来创建的。

特点进程线程协程
执行环境拥有独立的虚拟地址空间、堆、栈、数据段和代码段等资源与所属进程共享同一份地址空间和资源与线程相似,但拥有更轻量级的执行环境
资源管理每个进程有独立的资源管理机制,互不干扰与所属进程共享同一份资源,可以共享数据和代码段等资源与线程相似,但由程序自行管理和调度 ,虽然协程之间可以共享数据,但是并不会共享线程的资源,每个协程都有独立的栈空间和寄存器,因此协程之间是隔离的。
切换开销切换时需要保存和恢复进程的所有上下文,包括进程的虚拟地址空间、堆、栈、寄存器和状态等,开销较大切换时只需保存和恢复线程的栈、寄存器和状态等少量信息,开销较小切换时只需保存和恢复协程的栈、寄存器和状态等少量信息,开销更小
创建和销毁开销需要操作系统的介入,因为进程需要由操作系统来分配和管理资源,例如内存、文件描述符、信号量、共享内存等等,创建和销毁开销较大可通过 pthread 库等实现,开销相对较小由程序自行管理和调度,开销更小
并发处理能力能够实现真正的并行处理,但开销大可以实现并发处理,但需要考虑线程安全和同步机制可以实现轻量级的协程并发处理,但需要考虑协程之间的调度和通信机制
应用场景适用于需要独立执行和隔离资源的应用场景适用于需要实现多任务和并发处理的应用场景适用于需要实现高效的协作并发和异步处理的应用场景

注意:

  1. 相较于线程,协程的开销更小,因为协程的执行环境相对于线程更加轻量级。具体来说,协程相比线程少了以下开销:
  • 内存开销:协程拥有自己的栈空间,但栈的大小可以根据实际需要进行调整(在大部分操作系统中,线程栈的大小是固定的,无法根据实际需要进行调整。线程栈的大小在创建线程时就已经确定了,通常是在编译时就指定了默认的大小,也可以在创建线程时手动指定大小),因此协程的内存开销比线程更小。
  • 上下文切换开销:协程之间的切换只需要保存和恢复少量的状态信息(状态信息可以直接从寄存器中保存和加载,无需访问内存),如寄存器、栈指针等,而线程之间的切换需要保存和恢复整个执行上下文,包括程序计数器、寄存器、栈指针等,因此协程的上下文切换开销也比线程更小。
  • 调度开销:协程的调度是由程序自行管理和调度的,而线程的调度是由操作系统内核进行管理和调度的,因此协程的调度开销也比线程更小。