Go1.18调度器-标准库函数

发表于 · 归类于 代码 · 阅读完需 7 分钟 · 报告错误 · 阅读:

标准库函数

标准库里与调度相关的几个函数。了解实现细节,以便放心使用。

 

runtime.Gosched

让出当前 G 执行绪。与 gopark 的区别,G 会被放回队列,无需 goready 唤醒。

// 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)
}

// Gosched continuation on g0.
func gosched_m(gp *g) {
    // 继续运行 G0
    goschedImpl(gp)
}

 

// func mcall(fn func(*g))
// Switch to m->g0's stack, call fn(g).
// Fn must never return. It should gogo(&g->sched)
// to keep running g.
TEXT runtime·mcall<ABIInternal>(SB), NOSPLIT, $0-8
    MOVQ    AX, DX              // DX = fn
    
    // save state in g->sched
    MOVQ    0(SP), BX           // caller's PC
    MOVQ    BX, (g_sched+gobuf_pc)(R14)
    LEAQ    fn+0(FP), B         // caller's SP
    MOVQ    BX, (g_sched+gobuf_sp)(R14)
    MOVQ    BP, (g_sched+gobuf_bp)(R14)
    
    // switch to m->g0 & its stack, call fn
    MOVQ    g_m(R14), BX
    MOVQ    m_g0(BX), SI        // SI = g.m.g0
    CMPQ    SI, R14             // if g == m->g0 call badmcall
    JNE goodm
    JMP runtime·badmcall(SB)
goodm:
    MOVQ    R14, AX             // AX (and arg 0) = g
    MOVQ    SI, R14             // g = g.m.g0
    get_tls(CX)                 // Set G in TLS
    MOVQ    R14, g(CX)
    MOVQ    (g_sched+gobuf_sp)(R14), SP // sp = g0.sched.sp
    PUSHQ   AX                  // open up space for fn's arg spill slot
    MOVQ    0(DX), R12
    CALL    R12                 // fn(g)
    POPQ    AX
    JMP runtime·badmcall2(SB)
    RET

 

func goschedImpl(gp *g) {

    // 修改状态
    status := readgstatus(gp)
    if status&^_Gscan != _Grunning {
        dumpgstatus(gp)
        throw("bad g status")
    }
    casgstatus(gp, _Grunning, _Grunnable)

    // 解除 MP 绑定,放回全局队列
    dropg()
    lock(&sched.lock)
    globrunqput(gp)
    unlock(&sched.lock)

    // 当前 MP 执行其他任务
    schedule()
}

 

runtime.Goexit

终止当前任务。这与return返回调用堆栈上一级不同,而是直接结束这个任务。

在 Main Goroutine 调用 Goexit,它会等待其他 Gs 结束后才会崩溃。

注意,终止的是G函数执行,而不是MP。

// panic.go

// Goexit terminates the goroutine that calls it. No other goroutine is affected.
// Goexit runs all deferred calls before terminating the goroutine. Because Goexit
// is not a panic, any recover calls in those deferred functions will return nil.
//
// Calling Goexit from the main goroutine terminates that goroutine
// without func main returning. Since func main has not returned,
// the program continues execution of other goroutines.
// If all other goroutines exit, the program crashes.

func Goexit() {
    
    // panic, defer ...
    
    goexit1()
}
// proc.go

// Finishes execution of the current goroutine.
func goexit1() {
    mcall(goexit0)
}

// goexit continuation on g0.
func goexit0(gp *g) {

    // G 任务结束,清理后放回复用链表。
    casgstatus(gp, _Grunning, _Gdead)
    dropg()
    gfput(_p_, gp)
    
    // 当前 MP 继续执行其他任务。
    schedule()
}