Go 语言,并发控制神器之 Context
协程如何退出
一个协程启动后,一般是代码执行完毕,自动退出,但是如果需要提前终止怎么办呢?
一个办法是定义一个全局变量,协程中通过检查这个变量的变化来决定是否退出。这种办法须要加锁来保证并发安全,说到这里,有没有想的什么解决方案?
select + channel 来实现:
运行结果:
可以看到,每秒打印一次“认真摸鱼中,请勿打扰...”,3 秒后发出停止指令,程序进入 “下班咯~~~”。
Context 初体验
上面我们使用 select+channel 来实现了协程的终止,但是如果我们想要同时取消多个协程怎么办呢?如果需要定时取消又怎么办呢?
此时,Context 就需要登场了,它可以跟踪每个协程,我们重写上面的示例:
运行结果:
Context 介绍
Context 是并发安全的,它是一个接口,可以手动、定时、超时发出取消信号、传值等功能,主要是用于控制多个协程之间的协作、取消操作。
Context 接口有四个方法:
Deadline 方法:可以获取设置的截止时间,返回值 deadline 是截止时间,到了这个时间,Context 会自动发起取消请求,返回值 ok 表示是否设置了截止时间。
Done 方法:返回一个只读的 channel ,类型为 struct{}。如果这个 chan 可以读取,说明已经发出了取消信号,可以做清理操作,然后退出协程,释放资源。
Err 方法:返回 Context 被取消的原因。
Value 方法:获取 Context 上绑定的值,是一个键值对,通过 key 来获取对应的值。
最常用的是 Done 方法,在 Context 取消的时候,会关闭这个只读的 Channel,相当于发出了取消信号。
Context 树
我们并不需要自己去实现 Context 接口,Go 语言提供了函数来生成不同的 Context,通过这些函数可以生成一颗 Context 树,这样 Context 就可以关联起来,父级 Context 发出取消信号,子级 Context 也会发出,这样就可以控制不同层级的协程退出。
生成根节点
emptyCtx
是一个 int 类型的变量,但实现了 context 的接口。emptyCtx
没有超时时间,不能取消,也不能存储任何额外信息,所以emptyCtx
用来作为 context 树的根节点。但是我们一般不直接使用
emptyCtx
,而是使用由emptyCtx
实例化的两个变量(background 、todo),分别通过调用Background
和TODO
方法得到,但这两个 context 在实现上是一样的。
Background 和 TODO 方法区别:
Background
和TODO
只是用于不同场景下:Background
通常被用于主函数、初始化以及测试中,作为一个顶层的context
,也就是说一般我们创建的context
都是基于Background
;而TODO
是在不确定使用什么context
的时候才会使用。
生成树的函数
可以通过 context。Background() 获取一个根节点 Context。
有了根节点后,再使用以下四个函数来生成 Context 树:
WithCancel(parent Context):生成一个可取消的 Context。
WithDeadline(parent Context, d time.Time):生成一个可定时取消的 Context,参数 d 为定时取消的具体时间。
WithTimeout(parent Context, timeout time.Duration):生成一个可超时取消的 Context,参数 timeout 用于设置多久后取消
WithValue(parent Context, key, val interface{}):生成一个可携带 key-value 键值对的 Context。
Context 取消多个协程
如果一个 Context 有子 Context,在该 Context 取消时,其下的所有子 Context 都会被取消。
Context 传值
Context 不仅可以发出取消信号,还可以传值,可以把它存储的值提供其他协程使用。
示例:
运行结果:
Context 使用原则
Context 不要放在结构体中,需要以参数方式传递
Context 作为函数参数时,要放在第一位,作为第一个参数
使用 context。Background 函数生成根节点的 Context
Context 要传值必要的值,不要什么都传
Context 是多协程安全的,可以在多个协程中使用
版权声明: 本文为 InfoQ 作者【微客鸟窝】的原创文章。
原文链接:【http://xie.infoq.cn/article/a1f9dcdf3c892685d5c0c4844】。文章转载请联系作者。
评论 (1 条评论)