go context的使用场景

context常用场景

  • 超时取消

  • 上下文控制

在写http服务时,通常为每个请求创建一个 goroutine 以便更好的处理并发业务,同时每个goroutine可能会创建更多的子goroutine,如何退出所有衍生出来的goroutine,就用到了context。

context接口


type Context interface {
    // 返回Context的超时时间
    Deadline() (deadline time.Time, ok bool)
 
    // 在Context超时或取消时(即结束了)返回一个关闭的channel
    // 即如果当前Context超时或取消时,Done方法会返回一个channel,然后其他地方就可以通过判断Done方法是否有返回(channel),如果有则说明Context已结束
    // 故其可以作为广播通知其他相关方本Context已结束,请做相关处理。
    Done() <-chan struct{}
 
    // 返回Context取消的原因
    Err() error
    
    // 返回Context相关数据
    Value(key interface{}) interface{}
}

context.Background() 返回一个空的 Context,这个空的 Context 一般用于整个 Context 树的根节点。然后使用 context.WithCancel(parent) 函数,创建一个可取消的子 Context,然后当作参数传给 goroutine 使用,这样就可以使用这个子 Context 跟踪这个 goroutine。

WithCancel

func work(ctx context.Context, id int) {
	for {
		select {
		case <-ctx.Done():
			fmt.Printf("work%v结束退出\n", id)
			return
		default:
			fmt.Printf("work%v执行中\n", id)
			time.Sleep(time.Second * 2)
		}
	}
}

func main() {

	ctx, cancel := context.WithCancel(context.Background())

	go work(ctx, 1)
	go work(ctx, 2)

	time.Sleep(time.Second * 6)
	fmt.Println("取消work")
	cancel() // 收到cancel后 两个goroutine退出

	time.Sleep(time.Second * 4)

WithValue

package main
 
import (
	"context"
	"fmt"
	"time"
)
 
var key string = "name"
 
func run(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			fmt.Printf("任务%v结束退出\n", ctx.Value(key))
			return
		default:
			fmt.Printf("任务%v正在运行中\n", ctx.Value(key))
			time.Sleep(time.Second * 2)
		}
	}
}
 
func main() {
	//管理启动的协程
	ctx, cancel := context.WithCancel(context.Background())
	// 给ctx绑定键值,传递给goroutine
	valuectx := context.WithValue(ctx, key, "【监控1】")
 
	// 开启goroutine,传入ctx
	go run(valuectx)
 
	// 运行一段时间后停止
	time.Sleep(time.Second * 10)
	fmt.Println("停止任务")
	cancel() // 使用context的cancel函数停止goroutine
 
	// 为了检测监控过是否停止,如果没有监控输出,表示停止
	time.Sleep(time.Second * 3)

未完待续