93d19771b16cfcfcd970595166683cb2
Goroutine 切换的时候发生了什么?

Goroutine怎么主动让出权力?

参考:https://golang.org/pkg/runtime/#Gosched

Goroutine上下文切换的时候会发生什么?

跟进去,看 Gosched 的源码:

// Gosched yields the processor, allowing other goroutines to run. It does not
// suspend the current goroutine, so execution resumes automatically.
func Gosched() {
    mcall(gosched_m)
}

mcall:

// 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的作用是从g切到g0,然后执行fn(g)。这篇文章 里说过,g0是绑定在m上的一个g,使用系统栈。

我们接下来跟 gosched_m:

// Gosched continuation on g0.
func gosched_m(gp *g) {
    if trace.enabled {
        traceGoSched()
    }
    goschedImpl(gp)
}

然后是 goschedImpl(gp):

func goschedImpl(gp *g) {
    status := readgstatus(gp)
    if status&^_Gscan != _Grunning {
        dumpgstatus(gp)
        throw("bad g status")
    }
    casgstatus(gp, _Grunning, _Grunnable)
    dropg()
    lock(&sched.lock)
    globrunqput(gp)
    unlock(&sched.lock)

    schedule()
}

可以看到,让出权力的过程是:

  • 读取当前g的状态,将状态从 _Grunning 切换成 _Grunnable
  • 解除当前g和m的关系
  • 锁定全局调度器
  • 将这个g丢到全局g队列去
  • 解锁全局调度器
  • 调用 schedule 去寻找可执行的g

关于 schedule 的分析,看 这篇文章

g切换的时候,要做哪些事情?

如果你跟进了 schedule,会发现,找到了g之后,会执行 execute 函数:

```go
// Schedules gp to run on the current M.
// If inheritTime is true, gp inherits the remaining time in the
// current time slice. Otherwise, it starts a new time slice.
// Never returns.
//
// Write barriers are allowed because this is called immediately after
// acquiring a P in several places.
//
//go:yeswritebarrierrec
func execute(gp *g, inheritTime bool) {

top Created with Sketch.