context
包(context)类型是包装了channel通信来关联goroutine服务之间的控制机制,支持树状的上级控制一个或者多个下级,不支持反向控制和平级控制,同理参数的共享是树状的传递流动方向,也可以用来管理有超时依赖的函数,以下代码实例里面,存在forever for循环的才算服务。
参数共享特性
- context的参数存储不是map,是struct的key和val两个属性,对外暴露操作接口
- context的参数由父goroutine初始化,子goroutine调用
- 父context会嵌入子context,同名参数相当于有多个版本
- 子context获取参数会按照embed的向上查找机制获得多层父级context的参数,同名参数优先使用子context的参数版本
- 子context初始化非同名参数,相当于增加新参数
- 子context初始化同名参数,相当于增加一个参数版本
- context传递的参数注意选择必要的,简单的,size小的
- context因为只读所以是gorountine安全的,可以多个子gorountine服务共享同一个context的父控制
控制特性
- 父gorountine服务通过context控制子gorountine服务,由struct里面的空channel实现,父gorountine服务close空channel,子gorountine服务的select监听并可能触发关闭事件,结束生命周期
- 父gorountine服务达到某种状态,传递信号,结束子gorountine服务
- 父gorountine服务初始化启动子gorountine服务时,传入截止时刻或者超时时间
- 父gorountine结束,则所有的子gorountine服务结束
- 以上三条规则共同决定一个gorountine服务的生命周期
使用
|
|
子goroutine服务的关联的context是通过WithCancel, WithDeadline, WithTimeout, WithValue复制父goroutine服务的内容,派生出新的context。CancelFunc可以取消所有的后代,移除父goroutine服务在子goroutine服务的关联,并停止所有关联timer。
|
|
以上代码的Context链下图:
3秒超时:ctx4超时退出
5秒超时:ctx3超时退出,导致子节点ctx5和ctx6都退出
这里只是形象的表示了多级context之间形成的控制关系,实际业务里面ctx5,ctx6所在的goroutine如果在执行业务是不会立即退出的,只有当goroutine在等待IO事件等待数据的时候才会由select响应结束的case。
Background和TODO
都是返回一样的无控制条件无参数的empty Context,background在主服务里面通常作为匿名参数生成可控的context变量;todo可能会用于程序兼容处理,当空桩,做空调用
WithCancel
传入父Context,返回可控子Context,并且返回结束调用子context的goroutine的控制函数
|
|
WithDeadline
传入父Context和截止期限,返回包含截止期限的可控子Context,并且返回结束调用子context的goroutine的控制函数
|
|
WithTimeout
传入父Context和超时时间,返回包含超时时间的可控子Context,并且返回结束调用子context的goroutine的控制函数
|
|
WithValue
传入父Context和参数key-val,返回包含参数的子Context,如果父context是background,那么子context不可控,否则可控
- 在服务里面context.Background(),不适合作为有变量接收的WithValue的参数,可以是ctx := context.WithCancel(context.WithValue(context.Background(), k, “Go”))
- context的key是包内的全局变量,不可导出,避免冲突
- context的key的数据类型必须支持==和!=,类似于map的key,包括bool,number,string,pointer,channel,interface和包含上述类型的array,struct。不支持slice,map,function或者包含它们的类型。
- 存储的数据最好是type-safe
|
|
go vet tool可以检查CancelFuncs的使用情况。context参数不要包裹在struct等一些复杂的数据结构里面,context参数位置放在函数的第一个变量,context参数不要传递nil。
问题
如何实现平级控制,一个子goroutine出现问题同时取消共享父控制的其他子goroutine,参见golang.org/x/sync/errgroup
To avoid allocating when assigning to an interface{}, context keys often have concrete type struct{}. Alternatively, exported context key variables’ static type should be a pointer or interface. 这是什么意思?