chan + select 是go 中比较优雅结束一个 goroutine 的方式 但是当goroutine 衍生出其他更多的 goroutine 的时候怎么管理呢?
Context 就提供这么一种处理的方法 我看大佬们都称之为 上下文 0.0
context.Background()
返回一个空的Context
我们可以用这个 空的 Context 作为 goroutine 的root 节点(如果把整个 goroutine 的关系看作 树状)
使用context.WithCancel(parent)
函数,创建一个可取消的子Context
函数返回值有两个:子Context Cancel 取消函数
例如:
ctx, cancel := context.WithCancel(context.Background())
当要结束goroutine 的时候我们用函数 Cancel() 发送一个取消指令
<- ctx.Done() 如果接受到值 表示受到这个取消的指令
如果 多个 goroutine 用同一个 Context 作为参数 那么 只要这个 context 调用了 cancel 多个goroutine 就会同时受到 取消指令
假设有 Context 爸爸和 Context 儿子
如果 Context 爸爸关闭了 他的儿子也会受到 取消指令0.0 全都 GG 了
返回的 cancelfunc 可以取消一个Context,以及这个节点Context下所有的所有的Context,不管有多少层级。
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
Context的接口定义的比较简洁,我们看下这个接口的方法
这个接口共有4个方法,了解这些方法的意思非常重要,这样我们才可以更好的使用他们。
Deadline
方法是获取设置的截止时间的意思,第一个返回式是截止时间,到了这个时间点,Context会自动发起取消请求;第二个返回值ok==false时表示没有设置截止时间,如果需要取消的话,需要调用取消函数进行取消。
Done
方法返回一个只读的chan,类型为struct{}
,我们在goroutine中,如果该方法返回的chan可以读取,则意味着parent context已经发起了取消请求,我们通过Done
方法收到这个信号后,就应该做清理操作,然后退出goroutine,释放资源。
Err
方法返回取消的错误原因,因为什么Context被取消。
Value
方法获取该Context上绑定的值,是一个键值对,所以要通过一个Key才可以获取对应的值,这个值一般是线程安全的。
Context接口并不需要我们实现,Go内置已经帮我们实现了2个,我们代码中最开始都是以这两个内置的作为最顶层的partent context,衍生出更多的子Context。
var (
background = new(emptyCtx)
todo = new(emptyCtx)
)
func Background() Context {
return background
}
func TODO() Context {
return todo
}
参考于 http://www.flysnow.org/2017/05/12/go-in-action-go-context.html