Golang-协程调度器GMP

Golang协程调度器

一、Go调度器的由来

引言:

早期的单进程操作系统中,所有进程是顺序执行的,同一时刻只能有一个进程工作。

单进程时代的两个问题:1. 单一执行流程, 2. 进程阻塞带来CPU浪费


之后出现了多线程、多进程操作系统。CPU作为调度器,按时间片轮询所有线程,宏观上就实现了多进程同时执行。

多线程解决了阻塞问题,但带来了新的问题,线程的上下文切换是有成本的。进程或线程越多,切换成本就越大,CPU的资源浪费就越严重。同时,大量的创建线程或进程会消耗大量内存。

操作系统中,将一个线程内部分为用户态和内核态。那么,为啥那么不把这个线程一分为二,形成用户线程-内核线程的结构。我们开发者使用用户线程做接口调用之类的工作,内核线程来负责系统调用,管理硬件等。这样一个模型中,我们可以把内核空间的线程叫做Thread,而用户空间的线程就叫做协程 co-routine。

然而,如果是一个内核线程绑定一个协程,其实也并没有解决引言中所说的多线程带来的CPU浪费,内存浪费等问题。因此,在用户空间里,我们可以使用一个协程调度器,来管理多个协程,实现一个内核线程绑定多个协程。这个模型有点像Java NIO中的 Selector-Channel,我理解应该是同一种思想——多路复用嘛~

再多考虑一步,如果有一个协程阻塞了,那么后面的协程会变得无法执行。为此,我们可以更大胆的设计多线程对应多协程的一个模型,图我就不放了。那么压力此时就来到了这个协程调度器上了。其实,这样的模型和Netty的workGroup用多线程处理更多的通道事件是一样的 。还是那个思想——多路复用来提高利用率。

在Golang中,coroutine被改名为了goroutine,同时,一个goroutine的内存被控制在了4KB,因此Golang中可以创建超级大量的goroutine。

二、Goroutine调度器的GMP模型

2.1 GMP模型简介

GMP实际上就是三个符号,G代表goroutine,P代表processor,M代表thread。

上图就是GMP模型的示意图了,针对图中各个部分,做以下说明:

  • 全局队列:存放等待运行的G
  • P的本地队列:存放不超过256个的等待运行的G;优先将新创建的G放在P的本地队列,如果都满了就放在全局队列
  • P列表:程序启动时创建;最多有GOMAXPROCS个,可以配置;
  • M列表:操作系统分配给当前GO程序的内核线程数;可以通过SetMAXThreads函数设置;M有个线程池,可以在一个M阻塞时,创建一个新的M;在M空闲时,可以将该M回收

2.2 调度器的设计策略

  • 复用线程
    • 目的:避免频繁创建和销毁线程M
    • work stealing机制:当本线程无可运行的G时,先不销毁线程,而是从其他线程绑定的P里偷取G
    • hand off机制: 当本线程因为G进行系统调用而阻塞时,该线程释放绑定的P,把P交给其他空闲的线程M继续执行
  • 利用并行
    • GOMAXPROCS设置P的数量,充分利用多核CPU,实现并行处理
  • 抢占
    • 在coroutine中,一个协程要等待正在执行的另一个协程主动让出CPU才执行
    • 在go中,一个goroutine最多占用CPU10ms,然后就会被别的goroutine抢占CPU
  • 全局G队列
    • 当M执行work stealing时,如果从其他P偷不到G时,可以从全局队列获取G

2.3 go func() 经历了什么过程

​ 1、我们通过 go func()来创建一个goroutine;

​ 2、有两个存储G的队列,一个是局部调度器P的本地队列、一个是全局G队列。新创建的G会先保存在P的本地队列中,如果P的本地队列已经满了就会保存在全局的队列中;

​ 3、G只能运行在M中,一个M必须持有一个P,M与P是1:1的关系。M会从P的本地队列弹出一个可执行状态的G来执行,如果P的本地队列为空,就会想其他的MP组合偷取一个可执行的G来执行;

​ 4、一个M调度G执行的过程是一个循环机制;

​ 5、当M执行某一个G时候如果发生了syscall或则其余阻塞操作,M会阻塞,如果当前有一些G在执行,runtime会把这个线程M从P中摘除(detach),然后再创建一个新的操作系统的线程(如果有空闲的线程可用就复用空闲线程)来服务于这个P;

​ 6、当M系统调用结束时候,这个G会尝试获取一个空闲的P执行,并放入到这个P的本地队列。如果获取不到P,那么这个线程M变成休眠状态, 加入到空闲线程中,然后这个G会被放入全局队列中。


Golang-协程调度器GMP
http://kun98-liu.gihub.io/2022/08/18/Golang-协程调度器GMP/
Author
Liu Jiankun
Posted on
August 18, 2022
Licensed under