【Go笔记】协程

进程与线程

  • 一个进程可以有单个或者多个线程
  • 线程之间共享进程的内存等资源
  • 进程之间相互独立,有独立的内存空间,机器码,状态,资源描述符等
  • 开启一个新进程的开销比一个新线程大得多,且多进程之间通信困难
  • 操作系统调度CPU的最小单位是线程,单核CPU使用交织线程,多核可实现真正的并行处理

线程的上下文切换

  • 实际运行中,线程数量比CPU核心数多得多
  • 为了平衡每个线程的执行时间,操作系统需要再适当的时间通过定时器中断、IO设备中断、系统调用时来执行上下文切换
  • 线程上下文切换时,需要从操作系统用户态转向内核态,记录上一个线程的重要寄存器值、进程状态等信息,记录在操作系统栈控制块中
  • 切换到下一个线程时,需要加载CPU所需的重要寄存器值,然后从内核态转移到操作系统用户态。如果这俩线程不属于同一个进程,那么还要更新额外的状态信息以及内存地址空间,将新的页表导入内存
  • 进程间切换中最大的问题在于内存地址空间的切换导致的缓存失效,所以不同进程切换要显著慢于同一进程中不同线程的切换。当然,现代CPU使用快速上下文切换技术来解决不同进程之间缓存失效的问题

线程与协程

  • Go中,协程被认为是轻量级的线程,但是操作系统的内核其实感知不到写成的存在
  • 协程的管理依赖Go运行时自身提供的调度器,同时Go中的协程是从属于某一个线程的

调度方式

  • 协程是用户态的,管理依赖Go运行时调度器
  • Go中协程是属于某一线程的,对应关系为m:n,为多对多,一个协程也可以被分配到多个线程中执行

上下文切换速度

  • 协程速度要快于现成,是因为协程切换不用经过操作系统用户态与内核态之间的切换
  • Go中的协程切换只需要保留极少的状态和寄存器变量值
  • 线程切换速度为1~2微秒,协程切换速度为0.2微秒左右

调度策略

  • 线程的调度大部分是抢占式的,会定时强制执行上下文切换
  • 协程在一般情况下是协作式调度,完成自己的任务之后,可以主动将执行权限让给其他协程
  • 所以协程相比如进程可以更好地在规定时间内完成自己的工作

栈的大小

  • 线程的栈大小一般是在创建时指定的,为了避免栈溢出,通常会比较大,大大限制了线程的创建数量
  • 协程的栈大小是可以动态扩容,初始栈大小很小,因此在实践中可以将协程看做轻量的资源

并发与并行

  • 并发指结果,在一定时间段内把所有任务执行完即可,一般指单核处理多任务的抢占式执行
  • 并行指状态,在同一时间有多个任务同时执行
  • 至极的多核场景中,并发和并行常常是同时存在的,多核在并行地处理多个线程,单核的多个线程又在上下文切换中交替执行
  • Go语言中的协程依托于线程,而协程可能被分配到多个线程中,线程可能被并行会并发执行,因此协程可能被并行或并发执行。但是并发是更加常见的现象

主协程与子协程

  • main()函数是特殊的协程,是主协程;主协程退出时,程序直接退出,不会有任何输出
  • 主协程如果有需要的话,需要等待子协程都执行完才能退出

GMP模型浅谈

  • Go语言中最经典的模型之一,生动概括了线程与协程的关系
  • G - Goroutine,即Go中的协程
  • M - 实际的线程
  • P - Processor,逻辑处理器
  • 在任意时刻,一个P可能在本地包含多个G,一个P只能绑定一个M
  • 一个G并不是固定绑定同一个P,有很多情况会导致P中的G转移到其他P中
  • 一个P只能对应一个M,但是也不行固定一个M,一个M也可能转移到其他P中执行

【Go笔记】协程
https://study.0x535a.cn/go-note/go-routine/
Author
Stephen Zeng
Posted on
August 28, 2025
Licensed under