前言
首先解答上一篇文章一文带你快速入门context
中留下的疑惑,为什么要defer cancelFunc()
?
func main() {
parent := context.Background()
for i := 0; i < 100; i++ {
go doRequest(parent)
}
time.Sleep(time.Second * 10)
}
// doRequest 模拟网络请求
func doRequest(parent context.Context) {
ctx, _ := context.WithTimeout(parent, time.Second*5)
time.Sleep(time.Millisecond * 200)
go func() {
<-ctx.Done()
fmt.Println("ctx done!")
}()
}
看上面的代码,在main
函数中异步调用doRequest
函数,doRequest
函数中新建一个5s
超时的上下文,doRequest
函数的调用时长为200ms
![image-20210710222940035](https://i-blog.csdnimg.cn/blog_migrate/4edce958d4845fb7b9a1053a1f8f47ac.png)
可以看到,doRequest
的上下文时间范围远大于函数调用花费的时间,在函数结束后没有主动取消上下文,这会造成上下文泄露
所以,defer cancelFunc()
的目的是避免上下文泄露!!
主动调用cancelFunc是一个好习惯!
了解一下Context接口
type Context interface {
// [1] 返回上下文的截止时间
Deadline() (deadline time.Time, ok bool)
// 返回一个通道,当上下文结束时,会关闭该通道,此时 <-ctx.Done() 结束阻塞
Done() <-chan struct{}
// [2] 该方法会在上下文结束时返回一个not nil err,该err用于表示上下文结束的原因
Err() error
// 返回与key关联的上下文的value
Value(key interface{}) interface{}
}
[1]处
,当上下文没有设置截止时间时,调用Deadline
,返回结果值中,ok = false
func main() {
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
deadline, ok := ctx.Deadline()
fmt.Printf("ok = %v, deadline = %v\n&