Go context 原理简述

cancel()
context.Background()

每次用户请求到来时,向一组具有上下文关系的 goroutine 中分别传入ctx 参数,并分别监听ctx.Done()方法。

Done()
cancel()

正因为上述方式,一个request范围内所有 goroutine 运行时的取消能得到有效控制。

Go context 介绍

Background()WithCancel()WithDeadline()WithTimeout()WithValue()Done()
// (1) 顶层Context:Background():返回一个空的Context,它作为所有由此继承Context的根节点
func Background() Context {
}

// (2) context库中,有4个关键方法
// 带cancel返回值的Context,一旦cancel被调用,即取消该创建的context
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
}

// 带有效期cancel返回值的Context,即必须到达指定时间点调用的cancel方法才会被执行
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
}

// 带超时时间cancel返回值的Context,类似Deadline,前者是时间点,后者为时间间隔
// 相当于WithDeadline(parent, time.Now().Add(timeout)).
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
}

// (3) Context提供的 Done()方法
select { 
    case <-ctx.Done(): 
        // do some clean… 
        // 主动终止对当前请求信息的处理,释放资源并返回
}

注意:父节点Context可以主动通过调用cancel方法取消子节点Context,而 子节点Context只能被动等待。同时父节点Context自身一旦被取消(如其上级节点Cancel),其下的所有子节点Context均会自动被取消。

  • 代码示例:
    通过引入Context包,一个request范围内所有goroutine运行时的取消能得到有效控制
package main

import (
    "context"
    "fmt"
    "time"
)

func someHandler() {
    // 创建继承Background的子节点Context
    ctx, cancel := context.WithCancel(context.Background())
    // ctx, cancel := context.WithTimeout(context.Background(), 3 * time.Second) // 3秒后提前结束doSth()
    go doSth(ctx)

    //模拟程序运行 - Sleep 5秒
    time.Sleep(5 * time.Second) // 避免main()函数会提前结束
    cancel()
}

//每1秒work一下,同时会判断ctx是否被取消,如果是就退出
func doSth(ctx context.Context) {
    var i = 1
    for {
        time.Sleep(1 * time.Second)
        select {
        case <-ctx.Done():
            fmt.Println("done")
            return
        default:
            fmt.Printf("work %d seconds. \n", i)
        }
        i++
    }
}

func main() {
    fmt.Println("start...")
    someHandler()
    fmt.Println("end.")
}
context.TODO()