参考:
https://www.flysnow.org/2017/05/12/go-in-action-go-context.html
1、Context接口
Context的接口定义的比较简洁,我们看下这个接口的方法。
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
这个接口共有4个
方法,了解这些方法的意思
非常重要
,这样我们才可以更好的使用
他们。
1.1、Deadline方法
是获取设置的截止
时间的意思,
第一个返回式是
截止
时间,到了这个时间点
,Context会自动
发起取消请求
;第二个返回值ok==false时表示
没有
设置截止
时间,如果需要取消
的话,需要调用取消函数进行
取消。
1.2、Done方法
返回一个只读的chan,类型为struct{},
我们在goroutine中,如果该方法返回的chan可以
读取
(也就是阻塞状态结束),则意味
着parent context已经发起了取消请求,我们通过Done方法收到这个
信号
后,就应该做清理
操作,然后退出goroutine
,释放资源
。
1.3、Err方法
返回取消
的错误
原因,因为什么Context被取消。
1.4、Value方法
获取该Context上绑定
的值,是一个键值对
,所以要通过一个Key
才可以获取对应
的值,这个值一般是线程
安全的。
以上四个方法中常用的就是Done
了,如果Context取消的时候,我们就可以得到一个关闭
的chan,关闭的chan是可以读取
的,所以只要可以读取
的时候,就意味着收到Context取消的信号了,以下是这个方法的经典用法。
func Stream(ctx context.Context, out chan<- Value) error {
for {
v, err := DoSomething(ctx)
if err != nil {
return err
}
select {
case <-ctx.Done():
return ctx.Err()
case out <- v:
}
}
}
2、Context接口分析
并不需要我们实现,Go内置
已经帮我们实现
了2个,我们代码中最开始都是以这两个内置的作为最顶层
的partent context,衍生出更多的子Context
。
var (
background = new(emptyCtx)
todo = new(emptyCtx)
)
func Background() Context {
return background
}
func TODO() Context {
return todo
}
2.1、Background
一个是Background,主要用于main
函数、初始化以及测试代码中,作为Context这个树结构的最顶层
的Context,也就是根Context
。
2.2、TODO
一个是TODO,它目前还不知道
具体的使用
场景,如果我们不知道该使用
什么Context的时候,可以使用这个。
2.3、emptyCtx结构体类型
他们两个本质上都是emptyCtx结构体类型,是一个不可取消
,没有设置
截止时间,没有携带
任何值的Context。
type emptyCtx int
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
return
}
func (*emptyCtx) Done() <-chan struct{} {
return nil
}
func (*emptyCtx) Err() error {
return nil
}
func (*emptyCtx) Value(key interface{}) interface{} {
return nil
}
这就是emptyCtx
实现Context接口
的方法,可以看到
,这些方法什么都没做,返回的都是nil
或者零值
。