Go 高阶 の GMP 模型

一. Golang协程调度器得由来

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

在这里插入图片描述

在这里插入图片描述

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

简单点来说,携程就是用户级的多线程。

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

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

所有面临的两个问题,就是 CPU 的高消耗,和 内存的 高 占用。
在这里插入图片描述

1.2 Go 怎么做的?

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

在这里插入图片描述

我们可以直接把线程的上下两个部分给直接拆开
在这里插入图片描述
给他们起个别名,上边的就叫协程,通过协程调度器进行控制协程的切换
在这里插入图片描述
在这里插入图片描述

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

GMP 解释

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

在这里插入图片描述

  1. 全局队列:存放等待运行的 G

  2. P的本地队列

    • 存放等待运行的 G
    • 数量限制:不超过 256 G
    • 优先将新建的 G 放在 P 的本地队列中,如果满了就会放到全局队列中;这里的本地队列,就是比如 G2创建了 G3,那么G3就应该加在 G2所在的队列中。
  3. P列表

    • 程序启动时创建
    • 最多有 runtime.GOMAXPROCS() 个,可以配置
  4. M列表 : 当前操作系统分配到 Go 程序的内核线程数,他不是动态可变的。

    • m的数量go语言本身限制是 10000 个
    • 有一个阻塞,就会创建一个新的 M
    • 如果有 M 空闲,那么就会回收或者睡眠

调度器的设计策略

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

偷取工作

当本线程无可用的 G 时,尝试从其他线程捆绑的 P 偷取 G,而不是销毁线程。因为内核级线程的开辟和销毁时候相当的消耗资源的。
在这里插入图片描述

hand off 传球

当本线程因为 G 进行系统调用阻塞时,线程释放捆绑的 P,把 P 转移给其他空闲的线程执行。让这个 G和这个 M 就捆绑在一起阻塞着
在这里插入图片描述

当阻塞完成会怎么样?

当阻塞的 M完成的时候,M就会去找他原来的 P 看能不能再要回来,显然这样是不行的,这样话,他就会去P的队列里,有没有等待调用的,如果有,就拿过来,如果没有,这个 M 就加入到 休眠队列中,对应的 G就应该加入到全局对立中,等待被调用。

在这里插入图片描述

调度器得生命周期

在这里插入图片描述

参考文献

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

posted @ 2021-07-01 14:36  沧海一声笑rush  阅读(363)  评论(0编辑  收藏  举报