深入解析Golang之Context

本文深入解析了Golang的Context包,探讨了为何需要Context,通过源码分析展示了Context如何实现goroutine的控制、超时和取消功能。文章还介绍了Context的最佳实践,强调了在多层协程结构和微服务中Context的重要性,并提供了相关场景的应用示例。
摘要由CSDN通过智能技术生成

​context是什么

context翻译成中文就是上下文,在软件开发环境中,是指接口之间或函数调用之间,除了传递业务参数之外的额外信息,像在微服务环境中,传递追踪信息traceID, 请求接收和返回时间,以及登录操作用户的身份等等。本文说的context是指golang标准库中的context包。Go标准库中的context包,提供了goroutine之间的传递信息的机制,信号同步,除此之外还有超时(timeout)和取消(cancel)机制。概括起来,Context可以控制子goroutine的运行,超时控制的方法调用,可以取消的方法调用。

为什么需要context

根据前面的Context的介绍,Context可以控制goroutine的运行,超时、取消方法的调用。对于这些功能,有没有别的实现方法。当然是有的,控制goroutine的运行,可以通过select+channel的机制实现,超时控制也可以通过ticker实现,取消方法调用也可以向channel中发送信号,通知方法退出。既然Context能实现的功能,也有别的方式能够实现,那为啥还要Context呢?在一些复杂的场景中,通过channel等方式控制非常繁琐,而采用Context可以很方便的实现上述功能。场景1:主协程启动了m个子协程,分别编号为g1,g2,...gm。对于g1协程,它又启动了n个子协程,分别编号为g11,g12,...g1n。现在希望主协程取消的时候或g1取消的时候,g1下面的所有子协程也取消执行,采用channel的方法,需要申请2个channel, 一个是主协程退出通知的channel,另一个是g1退出时的channel。g1的所有子协程需要同时select这2个channel。现在是2层,用channel还能接受,如果层级非常深,那监控起来需要很多的channel, 操作非常繁琐。采用Context可以简单的达到上述效果,不用申请一堆channel。场景2: 在微服务中,任务A运行依赖于下游的任务B, 考虑到任务B可能存在服务不可用,所以通常在任务A中会加入超时返回逻辑,需要开一个定时器,同时任务A也受控于父协程,当父协程退出时,希望任务A也退出,那么在任务A中也要监控父协程通过channle发送的取消信息,那有没有一种方式将这两种情况都搞定,不用即申请定时器又申请channel,因为他们的目的都是取消任务A的运行嘛,Context就能搞定这种场景。

context源码解析

下面的源码解析的是go的最新版本1.14.2

结构图

context定义了2大接口,Context和canceler, 结构体类型*emptyCtx,*valueCtx实现了Context接口,*cancelCtx同时实现了Context接口和cancelr接口,*timerCtx内嵌了cancelCtx,它也间接实现了Context和canceler接口。类型结构如下

函数、结构体和变量说明

名称 类型 可否导出 说明
Context 接口 可以 Context最基本接口,定义了4个方法
canceler 接口 不可以 Context取消接口,定义了2个方法
emptyCtx 结构体 不可以 实现了Context接口,默认都是空实现,emptyCtx是int类型别名
cancelCtx 结构体 不可以 可以被取消
valueCtx 结构体 不可以 可以存储key-value信息
timerCtx 结构体 不可以 可被取消,也可超时取消
CancelFunc 函数 可以 取消函数签名
Background 函数 可以 返回一个空的Context,常用来作为根Context
Todo 函数 可以 返回一个空的 context,常用于初期写的时候,没有合适的context可用
WithCancel 函数 可以 理解为产生可取消Context的构造函数
WithDeadline 函数 可以 理解为产生可超时取消Context的构造函数
WithTimeout 函数 可以 理解为产生可超时取消Context的构造函数
WithValue 函数 可以 理解为产生key-value Context的构造函数
newCancelCtx 函数 不可以 创建一个可取消的Context
propagateCancel 函数 不可以 向下传递 context 节点间的取消关系
parentCancelCtx 函数 不可以 找到最先出现的一个可取消Context
removeChild 函数 不可以 将当前的canceler从父Context中的children map中移除
background 变量 不可以 包级Context,默认的Context,常作为顶级Context
todo 变量 不可以 包级Context,默认的Context实现,也作为顶级Context,与background同类型
closedchan 变量 不可以 channel struct{}类型,用于信息通知
Canceled 变量 可以 取消error
DeadlineExceeded 变量 可以 超时error
cancelCtxKey 变量 不可以 int类型别名,做标记用的

Context接口

Context具体实现包括4个方法,分别是Deadline、Done、Err和Value,如下所示,每个方法都加了注解说明。

// Context接口,下面定义的四个方法都是幂等的
type Context interface {
 // 返回这个Context被取消的截止时间,如果没有设置截止时间,ok的值返回的是false,
 // 后续每次调用对象的Deadline方法是,返回的结果都和第一次相同,即具有幂等性
 Deadline() (deadline time.Time, ok bool)
 
 // 返回一个channel对象,在Context被取消时,此channel会被close。如果没有被
 // 取消,可能返回nil。每次调用Done总是会返回相同的结果,当channel被close的时候,
 // 可以通过ctx.Err获取错误信息
 Done() <-chan struct{}
 
 // 返回一个error对象,当channel没有被close的时候,Err方法返回nil,如果channel被
 // close, Err方法会返回channel被close的原因,可能是被cancel,deadline或timeout取消
 Err() error
 
 // 返回此cxt中指定key对应的value
 Value(key interface{}) interface{}
}

canceler接口

canceler接口定义如下所示,如果一个Context类型实现了下面定义的2个方法,该Context就是一个可取消的Context。Context包中结构体指针*cancelCtx和*timerCtx实现了canceler接口。

  • 为啥不将这里的canceler接口与Context接口合并呢?况且他们定义的方法中都有Done方法,可以解释得通的说法是,源码作者认为cancel方法并不是Context必须的,根据最小接口设计原则,将两者分开。像emptyCtx和valueCtx不是可取消的&

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值