在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
Go并发之Context篇
前言介绍:在学习Go并发的时候,我们总是能够看到context,而这个context却只在go语言中存在。笔者在看到context的时候,便问了自己几个问题。
基于上面的这些问题,笔者做了整理,笔者觉得在知道了这些问题的答案之后,context 也算是有一点了解了。 1. 为什么Go需要context,它是用来干什么的? 原因:在golang中的创建一个新的协程并不会返回像c语言创建一个线程一样类似的pid,这样就导致我们不能从外部杀死某个线程,所以我们就得让它自己结束。(备注:goroutine不能返回pid的原因,应该是协程的实现原理有很大关系,多个协程对应1个线程的实现机制。) 当然我们可以采用channel+select的方式,来解决这个问题,不过场景很复杂的时候,我们就需要花费很大的精力去维护channel与这些协程之间的关系,这就导致了我们的并发代码变得很难维护和管理。例如:由一个请求衍生出多个协程,并且之间需要满足一定的约束关系,以实现一些诸如:有效期,中止线程树,传递请求全局变量之类的功能。 Context机制:context的产生,正是因为协程的管理问题,golang官方从1.7之后引入了context,用来专门管理协程之间的关系。 Google的解决方法是Context机制,相互调用的goroutine之间通过传递context变量保持关联,这样在不用暴露各goroutine内部实现细节的前提下,有效地控制各goroutine的运行。通过传递context就可以追踪goroutine 调用树,并在这些调用树之间传递通知和元数据。 虽然goroutine之间是平行的,没有继承关系,但是Context设计成是包含父子关系的,这样可以更好的描述goroutine调用之间的树型关系。 2. context的定义是什么样子的? context是上下文的意思,一般理解为程序单元的一个运行状态、现场、快照,其中包含函数调用以及涉及的相关的变量值。 每个Goroutine在执行之前,都要先知道程序当前的执行状态,通常将这些执行状态封装在一个Context变量中,传递给要执行的Goroutine中。上下文则几乎已经成为传递与请求同生存周期变量的标准方法。 下面是https://golang.org/pkg/context/中提供的接口和常用API: 2.1 接口简介
2.2 常用API func WithCancel(parent Context) (ctx Context, cancel CancelFunc) // 一旦调用cancel,就会取消创建的ctx func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) // 带有效期的cancel, 到期之后会主动调用cancel func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) // 带超时时间的cancel,与WithDeadline类似,区别在于下面的参数是时间间隔 3.我们如何正确使用context? step1:首先,需要创建context的根节点。 // 返回一个空的Context,它作为所有由此继承Context的根节点 func Background() Context (备注:这里创建的ctx,不能被取消、没有值、也没有过期时间,通常是在主协程或者第一个处理Request的协程中) step2: 创建下层context节点。 通过2.2中的API来创建下层context节点,而这里创建好的下层context节点具有以下特点: 1.父节点Context可以主动通过调用cancel方法取消子节点Context。 2.子节点Context只能被动等待。 3.父节点Context自身一旦被取消(如其上级节点Cancel),其下的所有子节点Context均会自动被取消。 例子1: 主协程主动调用cancel() 取消子context Output: 通过输出我们可以看出来,在主协程调用了cancel()之后,子协程中的ctx会被主动关闭掉,延迟时间是1秒,会看到打印done。 例子2: 超时之后,调用cancle()的例子 通过输出可以看出来,在2s超时之后,也就是done会主动打印出来,表明cancel()被主动调用了。(备注:warning提示可以被忽略掉,因为cancle()不被创建根ctx的协程主动调用就会提示这个告警。) 4.context是如何实现的呢? (图片来自:https://zhuanlan.zhihu.com/p/34417106) 1. context的存储与查询: context上下文数据的存储就像一个树,每个结点只存储一个key/value对。WithValue()保存一个key/value对,它将父context嵌入到新的子context,并在节点中保存了key/value数据。Value()查询key对应的value数据,会从当前context中查询,如果查不到,会递归查询父context中的数据。 备注:context中的上下文数据不是全局的,它只查询本节点及父节点们的数据,不能查询兄弟节点的数据。 2. cancel的实现: (图片来自:https://zhuanlan.zhihu.com/p/34417106) cancelCtx结构体中children保存它的所有子canceler, 当外部触发cancel时,会调用children中的所有cancel()来终止所有的cancelCtx。done用来标识是否已被cancel。当外部触发cancel、或者父Context的channel关闭时,此done也会关闭。 对于超时调用cancel(), 是因为timerCtx 中存储了一个超时时间,等到超时间到期之后,会主动调用cancel()。 调用cancel()之后的效果如下所示: (图片来自:https://zhuanlan.zhihu.com/p/34417106)
参考资料: Go进阶01:golang context 用法详解:https://mojotv.cn/2018/12/26/what-is-context-in-go Golang之Context的使用:http://www.nljb.net/default/Golang%E4%B9%8BContext%E7%9A%84%E4%BD%BF%E7%94%A8/ golang中Context的使用场景:https://www.cnblogs.com/yjf512/p/10399190.html Go Context的踩坑经历:https://zhuanlan.zhihu.com/p/34417106 |
请发表评论