文章目录

  • 一. Golang协程调度器得由来
  • 1.1多线程和多进程带来的弊端
  • 1.2 Go 怎么做的?
  • 二. Goroutine调度器的GMP模型设计思想
  • GMP 解释
  • 调度器的设计策略
  • 调度器得生命周期
  • 参考文献




一. Golang协程调度器得由来

1.1多线程和多进程带来的弊端

go maxprocess_Go

go maxprocess_多线程_02


以单核操作系统为例,根据时间片轮转机制,不同的线程就要不断的切换,那么 线程的数量越多,切换成本也就越大,也就越浪费,同样,多线程随着同步竞争(如锁、竞争资源冲突等),让开发变得越来越复杂

而且进程和线程占用内存比较大

  • 进程占用内存 虚拟内存 4GB
  • 线程占用内存 越 4MB

所有面临的两个问题,就是 CPU 的高消耗,和 内存的 高 占用。

go maxprocess_Go_03





1.2 Go 怎么做的?

正常的一个线程,是分为用户空间和内核空间的。

go maxprocess_多线程_04


我们可以直接把线程的上下两个部分给直接拆开

go maxprocess_Go_05


给他们起个别名,上边的就叫协程,通过协程调度器进行控制协程的切换

go maxprocess_go maxprocess_06


go maxprocess_go maxprocess_07





二. Goroutine调度器的GMP模型设计思想

GMP 解释

  • G:goroutine 协程
  • P:processor 处理器
  • M:thread 内核线程

go maxprocess_Go_08

  1. 全局队列:存放等待运行的 G
  2. P的本地队列
  • 存放等待运行的 G
  • 数量限制:不超过 256 G
  • 优先将新建的 G 放在 P 的本地队列中,如果满了就会放到全局队列中
  1. P列表
  • 程序启动时创建
  • 最多有 runtime.GOMAXPROCS() 个,可以配置
  1. M列表 : 当前操作系统分配到 Go 程序的内核线程数,他不是动态可变的。
  • m的数量go语言本身限制是 10000 个
  • 有一个阻塞,就会创建一个新的 M
  • 如果有 M 空闲,那么就会回收或者睡眠




调度器的设计策略

  • 复用线程
  • work stealing 机制
  • hand off 机制
  • 利用并行
  • GOMAXPROCS 限定 P 的个数
  • CPU核数/2
  • 抢占: 抢占可以理解为,每个CPU 最多分配10ms的时间,每个 goroutine 的优先级都是一样的。
  • 全局 G 队列:当其他的队列取不到的时候,就从全局队列里取一个,一般情况下,刚加入的 GO 会加入到本地队列里,而不会加入到全局队列中。

偷取工作:

当本线程无可用的 G 时,尝试从其他线程捆绑的 P 偷取 G,而不是销毁线程。

go maxprocess_go maxprocess_09

hand off 传球

当本线程因为 G 进行系统调用阻塞时,线程释放捆绑的 P,把 P 转移给其他空闲的线程执行。

go maxprocess_go maxprocess_10


go maxprocess_多线程_11





调度器得生命周期

go maxprocess_多线程_12





参考文献

[1]https://www.bilibili.com/video/BV19r4y1w7Nx?p=11&share_source=copy_web