context
一个接口、四种具体实现、六个函数
|
|
Deadline
返回绑定当前context
的任务被取消的截止时间;如果没有设定期限,将返回ok == false
。Done
当绑定当前context
的任务被取消时,将返回一个关闭的channel
;如果当前context
不会被取消,将返回nil
。Err
如果Done
返回的channel
没有关闭,将返回nil
;如果Done
返回的channel
已经关闭,将返回非空的值表示任务结束的原因。如果是context
被取消,Err
将返回Canceled
;如果是context
超时,Err
将返回DeadlineExceeded
。Value
返回context
存储的键值对中当前key
对应的值,如果没有对应的key
,则返回nil
emptyCtx
emptyCtx
是一个int
类型的变量,但实现了context
的接口。emptyCtx
没有超时时间,不能取消,也不能存储任何额外信息,所以emptyCtx
用来作为context
树的根节点。
但我们一般不会直接使用emptyCtx
,而是使用由emptyCtx
实例化的两个变量,分别可以通过调用Background
和TODO
方法得到,但这两个context
在实现上是一样的
Background
和TODO
只是用于不同场景下: Background
通常被用于主函数、初始化以及测试中,作为一个顶层的context
,也就是说一般我们创建的context
都是基于Background
;而TODO
是在不确定使用什么context
的时候才会使用。
valueCtx
|
|
valueCtx
利用一个Context
类型的变量来表示父节点context
,所以当前context
继承了父context
的所有信息;valueCtx
类型还携带一组键值对,也就是说这种context
可以携带额外的信息。valueCtx
实现了Value
方法,用以在context
链路上获取key
对应的值,如果当前context
上不存在需要的key
,会沿着context
链向上寻找key
对应的值,直到根节点。
WithValue
WithValue
用以向context
添加键值对:
|
|
这里添加键值对不是在原context
结构体上直接添加,而是以此context
作为父节点,重新创建一个新的valueCtx
子节点,将键值对添加在子节点上,由此形成一条context
链。获取value
的过程就是在这条context
链上由尾部上前搜寻:
why?
cancelCtx
|
|
跟valueCtx
类似,cancelCtx
中也有一个context
变量作为父节点;变量done
表示一个channel
,用来表示传递关闭信号;children
表示一个map
,存储了当前context
节点下的子节点;err
用于存储错误信息表示任务结束的原因。mu就是用来保护这几个字段的锁,以保障cancelCtx是线程安全的
WithCancel
WithCancel
函数用来创建一个可取消的context
,即cancelCtx
类型的context
。WithCancel
返回一个context
和一个CancelFunc
,调用CancelFunc
即可触发cancel
操作
|
|
timerCtx
timerCtx
是一种基于cancelCtx
的context
类型,从字面上就能看出,这是一种可以定时取消的context
。
|
|
WithDeadline
WithDeadline
返回一个基于parent
的可取消的context
,并且其过期时间deadline
不晚于所设置时间d
。
WithTimeout
与WithDeadline
类似,WithTimeout
也是创建一个定时取消的context
,只不过WithDeadline
是接收一个过期时间点,而WithTimeout
接收一个相对当前时间的过期时长timeout
https://juejin.cn/post/7053781262690942990
总结
context
主要用于父子任务之间的同步取消信号,本质上是一种协程调度的方式。另外在使用context
时有两点值得注意:上游任务仅仅使用context
通知下游任务不再需要,但不会直接干涉和中断下游任务的执行,由下游任务自行决定后续的处理操作,也就是说context
的取消操作是无侵入的;context
是线程安全的,因为context
本身是不可变的(immutable
),因此可以放心地在多个协程中传递使用。