七叶笔记 » golang编程 » 初探 Golang 之 context

初探 Golang 之 context

对熟悉golang的同学来说,相信对context肯定都不陌生,很多函数和方法的第一个参数就是「ctx context」,但是我们是否真的了解了context,什么时候该用context呢?

首先看一下源码是怎样实现和定义的,在context包中给出了context的定义,context在API之间会携带截止时间、取消信号以及值,可以同时被多个goroutine调用, 然后它本身是一个接口,包含四个方法,分别是

Deadline方法会返回此context设置的任务完成时间也就是取消的时间,当没有设置deadline的时候,返回值的ok会返回false,多个对Deadline的调用会返回相同的结果。

Done方法会返回一个channel,当此context的任务完成被取消时,channel会被关闭,对于永远不会取消的方法,done会返回nil,多次成功的调用会返回同一个结果。

Err方法的返回和Done相关联,当done未关闭时返回nil,当done关闭时,它会返回一个不为空的error解释原因。

Value方法会返回此context所对应key的值,如果没有所对应的key,那么会返回nil,多个相同的调用会返回同样的结果,这个方法被设计用来在API之间传递数据,比如token,traceid等。

context包还提供了一些方法,可以从已经存在的context中派生新的context,然后它们就组成了一个树形结果,当父context被取消的时候,所有基于它的context都会被取消, Background方法产生的是最原始的context,永远不会被取消。

首先看一下下面这个例子

 func main() {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()

go handle(ctx, 500*time.Millisecond)
select {
case <-ctx.Done():
fmt.Println("main", ctx.Err())
}
}

func handle(ctx context.Context, duration time.Duration) {
select {
case <-ctx.Done():
fmt.Println("handle", ctx.Err())
case <-time.After(duration):
fmt.Println("process request with", duration)
}
}  

我们创建了一个过期时间为1s的上下文,然后传入handle函数,它需要500ms来完成工作,

因为500ms小于1s,所以上面会输出

 process request with 500ms
main context deadline exceeded  

如果我们把handle时间调整到1500ms,那么就会输出

 main context deadline exceeded
handle context deadline exceeded  

因为处理时间不够,就会进入select超时的分支。

对于context的使用,还有一些要注意的地方

  • context不应该放在struct里面,而应该是一个单独的参数
  • context通常都是第一个参数,简写为ctx
  • 涉及到网络调用时最好都使用context进行超时控制
  • context Value不应该用来传递参数

相关文章