context1

栏目: Go · 发布时间: 6年前

内容简介:Context 通常被译作上下文,一般理解为程序单元的一个运行状态、现场、快照,而翻译中上下文又很好地诠释了它的本质,上下则是指存在上下层的传递,上会把内容传递给下。在 Golang 中,程序单元也就是指的 goroutine。每个 goroutine 在执行之前,都要先知道程序当前的执行状态,通常将这些执行状态封装在一个 Context 变量中,传递给要执行的 Goroutine 中。上下文则几乎已经成为传递与请求同生存周期变量的标准方法。context 包不仅实现了在程序单元之间共享状态变量的方法,同时

Context 通常被译作上下文,一般理解为程序单元的一个运行状态、现场、快照,而翻译中上下文又很好地诠释了它的本质,上下则是指存在上下层的传递,上会把内容传递给下。

在 Golang 中,程序单元也就是指的 goroutine。每个 goroutine 在执行之前,都要先知道程序当前的执行状态,通常将这些执行状态封装在一个 Context 变量中,传递给要执行的 Goroutine 中。上下文则几乎已经成为传递与请求同生存周期变量的标准方法。

context 包不仅实现了在程序单元之间共享状态变量的方法,同时能通过简单的方法,使我们在被调用程序单元的外部,通过设置 ctx 变量值,将过期或撤销这些信号传递给被调用的单元。在网络编程中,若存在A调用B的API,B再调用C的API,若A调用B取消,则也要取消B调用C,通过在A、B、C的API调用之间传递Context,以及判断其状态,就能解决此问题,这是为什么 gRPC 的接口中带上 ctx context.Context 参数的原因之一。

context 包的核心就是 Context 接口,其定义如下:

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key interface{}) interface{}
}
  • Deadline 会返回一个超时时间,Goroutine 获得了超时时间后,例如可以对某些 io 操作设定超时时间。
  • Done 方法返回一个信道(channel),当 Context 被撤销或过期时,该信道是关闭的,即它是一个表示 Context 是否关闭的信号。
  • 当 Done 信道关闭后,Err方法表明 Context 被撤销的原因。
  • Value 可以让 Goroutine 共享一些数据,当然获得数据是协程安全的。但是使用这些数据的时候,需要注意同步,比如返回了一个 map,而这个 map 的读写则要加锁。

Context 接口没有提供方法来设置其值和过期时间,也没有提供方法直接将其自身撤销。也就是说,Context 不能改变和撤销自身。那么该怎么通过 Context 传递改变后的状态呢?

context 使用

无论是 Goroutine,他们的创建和调用关系总是像层层调用进行的,就像人的辈分一样,而更靠顶部的 Goroutine 应有办法主动关闭其下属的 Goroutine 的执行(不然程序就可能失控了)。为了实现这种关系,Context 结构也应该是树状,叶子节点总由根节点衍生出来。

要创建 Context 树,第一步就是要得到根节点, context.Background 函数的返回值就是根节点:

func Background() Context

该函数返回空的 Context,该 Context 一般由接收请求的第一个 Goroutine 创建,是与进入请求对应的 Context 根节点,它不能取消、没有值、也没有过期时间。它常常作为处理 Request 的顶层 context 存在。

有了根节点,又该怎么创建其他的子节点、孙节点? context 包为我们提供了多个函数来创建他们:

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key interface{}, val interface{}) Context

函数接收一个 Context 类型的参数 parent,并返回一个 Context 类型的值,这样就层层创建出不同的节点。子节点是从复制父节点得到的,并且根据接收参数设定子节点的一些状态值,接着就可以将子节点传递给下层的 Goroutine 了。

再回到之前的问题:该怎么通过 Context 传递改变后的状态呢?使用 Context 的 Goroutine 无法取消某个操作,其实是合理的,因为这些 Goroutine 是被某个父 Goroutine 创建的,而理应只有父 Goroutine 可以取消操作。在父 Goroutine 中可以通过 WithCancel 方法获得一个 cancel 方法,从而获得 cancel 的权利。

第一个 WithCancel 函数,它是将父节点复制到子节点,并且还返回一个额外的 CancelFunc 函数类型变量,该函数类型的定义为:

type CancelFunc func()

调用 CancelFunc 对象将撤销对应的 Context 对象,这就是主动撤销 Context 的方法。在父节点的 Context 所对应的环境中,通过 WithCancel 函数不仅可创建子节点的 Context,同时也获得了该节点 Context 的控制权,一旦执行该函数,则该节点 Context 就结束了,则子节点需要类似如下代码来判断是否已结束,并退出该 Goroutine:

select {
case <-ctx.Done():
    // do some clean ...
}

WithDeadline 函数的作用也差不多,它返回 Context 的类型同样是 parent 的副本,但其过期时间由 deadline 和 parent 的过期时间同时决定。当 parent 的过期时间早于传入的 deadline 时间时,返回的过期时间应与 parent 相同。父节点过期时,其所有的子孙节点必须同时关闭;反之,返回的父节点的过期时间则为 deadline。

WithTimeout 函数与 WithDeadline 类似,只不过它传入的是从现在开始 Context 剩余的生命时长。他们都同样返回了所创建的子 Context 的控制权,一个 CancelFunc 类型的函数变量。

当顶层的 Request 请求函数结束后,我们就可以 cancel 掉某个 context,从而层层 Goroutine 根据判断 ctx.Done() 来结束。

WithValue 函数,它返回 parent 的一个函数副本,调用该副本的 Value(key) 方法将得到 val。这样,我们不光将根节点的原有的值保留了,还在孙节点中加入了新的值,注意若存在Key相同,则会被覆盖。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Hit Refresh

Hit Refresh

Satya Nadella、Greg Shaw / HarperBusiness / 2017-9-26 / USD 20.37

Hit Refresh is about individual change, about the transformation happening inside of Microsoft and the technology that will soon impact all of our lives—the arrival of the most exciting and disruptive......一起来看看 《Hit Refresh》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具