在上篇文章中,我们介绍了G
、M
和P
的数据结构,其中M数据结构中第一个字段是 g0
,这个字段也是一个 goroutine
,但和普通的 goroutine
有所区别,它主要用来实现对 goroutine 进行调度,下面我们将介绍它是如何实现调度groutine的。
另外还有一个 m0
, 它是一个全局变量,与 g0
的区别如下

本文主要翻译自 Go: g0, Special Goroutine 一文,有兴趣的可以查阅原文,作者有一系列高质量的文章推荐大家都阅读一遍。ℹ️ 本文基于 Go 1.13。
我们知道在Golang中所有的goroutine
的运行都是由调度器
来负责管理的,go调度器尝试为所有的goroutine
来分配运行时间,当有goroutine
被阻塞或终止时,调度器会通过对goroutine
进行调度以此来保证所有CPU都处于忙绿状态,避免有CPU空闲时间浪费时间。
goroutine 切换规则
在此之前我们需要记住一些goroutine切换规则。runtime源码
// src/runtime/stubs.go // mcall switches from the g to the g0 stack and invokes fn(g), // where g is the goroutine that made the call. // mcall saves g's current PC/SP in g->sched so that it can be restored later. // It is up to fn to arrange for that later execution, typically by recording // g in a data structure, causing something to call ready(g) later. // mcall returns to the original goroutine g later, when g has been rescheduled. // fn must not return at all; typically it ends by calling schedule, to let the m // run other goroutines. // // mcall can only be called from g stacks (not g0, not gsignal). // // This must NOT be go:noescape: if fn is a stack-allocated closure, // fn puts g on a run queue, and g executes before fn returns, the // closure will be invalidated while it is still executing. func mcall(fn func(*g))
对 mcall()
函数注释的翻译请参考文章 Runtime: 当一个goroutine 运行结束后会发生什么
一、将一个运行中的 Goroutine 切换到另一个的过程涉及到两个切换:
- 将运行中的
g
切换到g0
- 将
g0
切换到下一个将要运行的g

二、在 Go 中,goroutine
的切换成本很低,每次切换都需要对当前G的状态(PC/SP
)进行存储(g.sched
),以便下次恢复运行时读取当前G的上下文信息: