Golang之go的调度

"go, use go"

Posted by Keal on June 16, 2021

Go调度

系统级别的调度依然是 进程和线程.虽然线程比较轻量,但是在调度时也有比较大的额外开销。每个线程会都占用 1M 以上的内存空间,在切换线程时不止会消耗较多的内存,恢复寄存器中的内容还需要向操作系统申请或者销毁资源,每一次线程上下文的切换都需要消耗 ~1us 左右的时间1,但是 Go 调度器对 Goroutine 的上下文切换约为 ~0.2us,减少了 80% 的额外开销2

图-进程和线程

process-and-threads

Go是如何做到的呢?

  1. Go 语言的调度器通过使用与 CPU 数量相等的线程减少线程频繁切换的内存开销
  2. 在每一个线程上执行额外开销更低的 Goroutine 来降低操作系统和硬件的负载。

图-Goroutine和线程

goroutines-on-thread

图-Go语言调度器G-M-P模型

golang-scheduler

  1. G — 表示 Goroutine,它是一个待执行的任务;
  2. M — 表示操作系统的线程,它由操作系统的调度器调度和管理;
  3. P — 表示处理器,它可以被看做运行在线程上的本地调度器;

P作为调度器,核心的调度策略由两个队列维护.一个是全局的队列,一个是本地的队列.全局队列用来处理本地队列为空或者满的P. 为空时会从全局队列中”偷”取,满时会将新加入的goroutie放入全局队列

img

goroutie在执行过程中,碰到一些阻塞式的调用时,另一个goroutie会被执行. 而goroutie的处理则根据定义的各种状态来判断处理.

  1. blocking syscall (for example opening a file)
  2. network input
  3. channel operations
  4. primitives in the sync package.

待续.(涉及到一些协程和线程的状态流转和策略比较复杂.后续明白了再更吧 :confused:)

Reference

golang的系列在draveness的博客中有一些列非常详细的介绍.本篇都只是描述一些个人认为的重点内容.如果想知道相关golang设计过程,源码,机器执行指令等内容.推荐直接看draveness的内容或者其他更为详细的博文.

https://draveness.me/golang/