Golang 中 context 包使用

context 可以说是Golang中非常重要的包了,于是乎学习了一番,总结以作备忘。

文章包含以下内容:

  • context 是什么
  • context 的作用及使用姿势
  • context 使用时要注意什么

context 是什么

Go 1.7 标准库引入context,中文译作“上下文”,确切的说它是 goroutine 的上下文,包含了 goroutine 的运行状态、环境、现场等信息。

type Context interface {
	Deadline() (deadline time.Time, ok bool)
	Done() <-chan struct{}
	Err() error
	Value(key interface{}) interface{}
}

该接口定义了四个需要实现的方法:

  • Deadline:获取当前 context 的取消时间;
  • Done:返回一个只读的 channel,类型为结构体。这个 Channel 会在当前工作完成或者上下文被取消后关闭,多次调用 Done 方法会返回同一个 channel;
  • Err:获取当前 context 被取消的原因;
  • Value:获取当前 context 对应所存储的上下文信息。

context 的作用及使用姿势

context 主要作用在多个 goroutine 组成的树中同步取消信号以减少对资源的消耗和占用,同时它也有传值的功能。

取消 goroutine

协程中使用 select,来监听‘父协程’(这个的父协程是虚指代,也可能是父父协程)是否结束、取消。

代码如下:

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	go goroutine1(ctx)
	go goroutine2(ctx)

	time.Sleep(time.Second * 3)
	cancel()
	time.Sleep(time.Second * 3)
}

func goroutine1(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		case <-time.After(time.Second):
			fmt.Println("this is one")
		}
	}
}

func goroutine2(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		case <-time.After(time.Second):
			fmt.Println("this is two")
		}
	}
}

输出结果:

Written@liqilin01 go_code$ go run test004.go 
this is two
this is one
this is one
this is two
传递数据

传递数据也是一个很重要的功能,比如在一次请求的生命周期中传递用户的信息

代码如下:

// 写入
userInfo := map[string]interface{} {
		"user_id":       int64(1),
		"display_name": "大脑斧",
		"avatar":        "---",
		"phone":         "---",
}
newCtx := context.WithValue(ctx, "user_info", userInfo)

// 使用
userInfo := ctx.Value("user_info").(map[string]interface{})

注意事项

在官方文档中,对于使用 context 提出了几点建议:

  1. Do not store Contexts inside a struct type; instead, pass a Context explicitly to each function that needs it. The Context should be the first parameter, typically named ctx.
  2. Do not pass a nil Context, even if a function permits it. Pass context.TODO if you are unsure about which Context to use.
  3. Use context Values only for request-scoped data that transits processes and APIs, not for passing optional parameters to functions.
  4. The same Context may be passed to functions running in different goroutines; Contexts are safe for simultaneous use by multiple goroutines.
  • 不要将 Context 塞到结构体里。直接将 Context 类型作为函数的第一参数,而且一般都命名为 ctx。
  • 不要向函数传入一个 nil 的 context,如果你实在不知道传什么,标准库给你准备好了一个 context:todo。
  • 不要把本应该作为函数参数的类型塞到 context 中,context 存储的应该是一些共同的数据。例如:登陆的 session、cookie 等。
  • 同一个 context 可能会被传递到多个 goroutine,不过没关系,context 是并发安全的。

思考

  • context 是并发安全的 -> 因为使用了锁
// 以移除 child 为例
func removeChild(parent Context, child canceler) {
	p, ok := parentCancelCtx(parent)
	if !ok {
		return
	}
	p.mu.Lock()
	if p.children != nil {
		delete(p.children, child)
	}
	p.mu.Unlock()
}

参考

  • https://qcrao91.gitbook.io/go/biao-zhun-ku/context
  • https://draveness.me/golang/docs/part3-runtime/ch06-concurrency/golang-context/
  • https://mp.weixin.qq.com/s/5JDSqNIimNrgm5__Z4FNjw
  • https://mp.weixin.qq.com/s/A03G3_kCvVFN3TxB-92GVw
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值