GMP模式

当Go程序启动时,会为机器上的每一个虚拟核分配一个逻辑处理器(P)。同时,如果你的CPU处理器支持超线程技术,每一个超线程对于Go程序都是一个虚拟核

YzM0T1.jpg

上面是我的MacBook Pro的配置,可以看到有一个双核的CPU处理器。同时Intel Core i5支持超线程技术,每个CPU物理核允许两个线程同时不冲突地使用该CPU物理核。那么对于Go程序,就有4个虚拟核可以并行地运行系统线程

package main

import (
    "fmt"
    "runtime"
)

func main() {

    // NumCPU returns the number of logical
    // CPUs usable by the current process.
    fmt.Println(runtime.NumCPU())
}

NumCPU()会输出逻辑处理器(P)的数目。

每一个P会被分配一个系统线程(M),线程的调度是由系统调度器(OS Schedule)来负责。

G协程(Goruntine),你可以理解为用户态的线程,和线程不同的是,协程的调度由用户的应用程序来负责,多个协程间的上下文切换是发生在运行这些协程的线程上。

Go还维护这两个不同的运行队列全局队列(GRQ)本地队列(LRQ)。每一个P都会有一个LRQ,队列里的协程G,会进行上下文切换,在被绑定到P的线程M上运行。

GRQ保存着还没分配给P的协程,GRQ的协程会在后面被移动到某个PLRQ

Yzt8Yj.png

YzWVQf.jpg

可以看到,Go调度器的设计,复用了线程,减少了传统多线程上下文切换,带来的时间和资源的损耗。Go协程间的调度发生在用户态,不需要内核的参与,大大减少了时间延迟。

协程的状态

和线程一样,协程有三种状态,Go调度器的调度依赖协程的状态,所以有必要先大概了解下。

  • 等待(Waiting)
    协程暂停,等待条件满足后继续执行。一般是等待系统调用完成,或者同步调用(锁释放等)。
  • 可运行(Runnable)
    协程可运行,协程此时已经准备好,随时可以被分配到P线程M运行。
  • 执行中(Executing)
    协程正在P上的线程M上运行。

协程的上下文切换

什么时候当前执行中的协程可能会让出执行权,发生上下文切换,让可运行的协程获得执行权。有四种场景。

  • 使用关键字go
    关键字go创建新的协程,一旦新协程创建成功,go调度器会做出调度的决定。一般情况下,为了局部性优化,会将新创建的协程加入到同一个

PLRQ
YzrH3D.png

  • 垃圾回收
    go使用单独的协程来进行垃圾回收,垃圾回收可能会造成协程的切换。例如调度器会将使用到正在被回收堆的协程,让出执行权,切换到不使用正在被回收堆的协程。
  • 系统调用
    如果一个协程调用了系统调用,导致进程被阻塞(协程是碰着阻塞式IO会导致整个线程被挂起么?
    Yzrn6H.jpg
    这时候,调度器会将被阻塞的线程M1P解绑,但原协程G1还是会绑定在原线程M1。调度器会创建一个新的线程M2(或者使用之前创建但没销毁的线程)绑定到PP会从LRQ队列里挑选一个协程来运行。
    YzgrSf.png

    Yzg6Og.png

    当协程G1的系统调用完成后,G1可以被放回到原PLRQ,等待下一次协程切换来运行。
    YzgRTs.png

    但其实这里面会有一个很大的问题,如果每次系统调用都会创建一个新的线程,那么整个系统最终会创建越来越多的线程。
    一个很常见的go服务器端程序。每次与客户端创建一个连接后,都开启一个协程来处理连接的读写。
    YzRS5n.jpg

    go对于非阻塞的I/O调用,使用了网络轮询器,不需要每次都创建一个新的线程。
    网络轮询器是基于epoll实现,整个实现,个人理解,是将所有监听的套接字注册到epoll,利用I/O多路复用,等待套接字就绪后,协程可以读写套接字了,再将协程返回到PLRQ队列
    YzRZVJ.png

    YzRuP1.png

    YzRtIA.png

    下面有两篇文章,可以深入了解下网络轮询器的实现。
    The Go netpoller
    网络轮询器

  • 同步调用
    锁或者通道等操作,会造成协程阻塞,此时调度器会进行协程切换。

Go 调度器调度场景过程全解析

Go 调度器调度场景过程全解析 这篇文章里对Go的整个调度器调度场景,有非常详细的图和文字说明,很不错

参考资料

Scheduling In Go : Part II - Go Scheduler

Golang 调度器 GMP 原理与调度全分析

Last modification:May 24th, 2020 at 02:46 pm
如果觉得我的文章对你有用,请尽情赞赏 🐶