深入理解golang:Context

在golang中,最主要的一个概念就是并发协程 goroutine,它只需用一个关键字 go 就可以开起一个协程,并运行。

一个单独的 goroutine运行,倒也没什么问题。如果是一个goroutine衍生了多个goroutine,并且它们之间还需要交互-比如传输数据,那彼此怎么传输数据呢?如果一个子goroutine取消了,要取消跟其相关的goroutine,怎么样才可以做到?

比如说:在go web服务器中,每个请求request都是在一个单独的goroutine进行,这些
goroutine可能又开启其他的goroutine进行其他操作,那么多个goroutine之间怎么传输数据、遇到了问题怎么取消goroutine?

深入理解golang:Context

有时在程序开发中,每个请求用一个goroutine去处理程序,然而,处理程序时往往还需要其他的goroutine去访问后端数据资源,比如数据库、RPC服务等,这些goroutine都在处理同一个请求,所以他们需要访问一些共享资源,如用户身份信息、认证token等,如果请求超时或取消,与此请求相关的所有goroutine都应该退出并释放资源。

由于golang里没有像C语言中线程id类似的goroutine id,所以不能通过id直接关闭goroutine。但是有其他的方法。

解决方法:

用时间来表示过期、超时

用信号来通知请求该停止了

用channel通知请求结束

为此,golang给我们提供了一个简单的操作包:Context 包。

二、Context是什么

golang中的Context包,是专门用来简化对于处理单个请求衍生出多个goroutine,goroutine之间传输数据、取消goroutine、超时控制等相关操作的一个包。

三、Context功能

3.1 控制goroutine退出

及时退出 WithCancel

时间点退出 WithDeadline

时间间隔退出 WithTimeout

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

WithCancel,绑定一个parent,返回一个cancelCtx的Context,用返回的 CancelFunc 就可以主动关闭Context。一旦cancel被调用,即取消该创建的Context。

func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)

WithDeadline,带有效期的cancelCtx的Context,即到达指定时间点调用CancelFunc方法才会执行

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)

WithTimeout,带有超时时间的cancelCtx的Context,它是WithDeadline的封装,只不过WithTimeout为时间间隔,Deadline为时间点。

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {     return WithDeadline(parent, time.Now().Add(timeout)) }

3.2 设置值

func WithValue(parent Context, key, val interface{}) 四、源码分析

go version go1.13.9

4.1 整体程序分析

/src/context/context.go

重要接口

Context:定义了Context接口的4个方法。

canceler:context接口取消,定义了2个方法。

重要结构体

emptyCtx:实现了Context接口,它是个空的context,它永远不会被取消,没有值,没有deadline。其主要作为context.Background()和context.TODO()返回这种根context或者不做任何操作的context。如果用父子关系来理解,emptyCtx就是用来创建父context。

cancelCtx:可以被取消

timerCtx:超时会被取消

valueCtx:可以存储k-v键值数据

重要函数

Backgroud:返回一个空的context,常用作根context

TODO:返回一个空的context,常用语重构时期,没有合适的context可用

newCancenCtx:创建一个可取消的context

parentCancelCtx:找到第一个可取消的父节点

WithCancel:基于父contxt,生成一个可取消的context

WithDeadline:创建一个带有截止时间的context

WithTimeout:创建一个带有过期时间的context

WithValue:创建一个存储键值对k-v的context

Background 与 TODO 用法有啥区别呢?

看函数其实它们俩没多大区别,只是使用和语义上有点区别:

Background:是上下文默认值,所有其他上下文都应该从它衍生出来

TODO:只是在不确定该使用哪种上下文时使用

4.2 Context接口 type Context interface { // Deadline返回一个到期的timer定时器,以及当前是否以及到期 Deadline() (deadline time.Time, ok bool) // Done在当前上下文完成后返回一个关闭的通道,代表当前context应该被取消,以便goroutine进行清理工作 // WithCancel:负责在cancel被调用的时候关闭Done // WithDeadline: 负责在最后其期限过期时关闭Done // WithTimeout:负责超时后关闭done Done() <-chan struct{} // 如果Done通道没有被关闭则返回nil // 否则则会返回一个具体的错误 // Canceled 被取消 // DeadlineExceeded 过期 Err() error // 返回对应key的value Value(key interface{}) interface{} }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsffzy.html