timerCtx类型
timerCtx类型实现了Context的所有接口,并且其内部嵌入了一个cancelCtx类型的字段,让其拥有了cancelCtx类型的特性,并且加入了基于时间点的判断。
在Context标准库中,返回timerCtx类型的函数有两个:
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
其中,WithTimeout是依托于WithDeadline实现的。后续会详细讲解这两个函数的实现。
二者的区别是:
WithDeadline传入一个具体的时间节点,如2025-01-22 15:04:05.123456789 +0000 UTC , 而其会在该时间点自动cancel。
WithTimeout传入一个时间段,如2 * time.Second , 而起会在2秒后自动cancel。
timerCtx的成员
timerCtx类型的成员如下:
type timerCtx struct {
*cancelCtx
timer *time.Timer // Under cancelCtx.mu.
deadline time.Time
}
*cancelCtx
timerCtx类型内部嵌入了一个cancelCtx类型的字段,让其可以实现cancelCtx中的方法,让其可以不用顾虑取消信号的传递的实现与父子节点的处理,可以让timerCtx专注于增加时间点判断功能的实现。符合装饰器模式的思想。
timer *time.Timer
用于处理时间点过后执行的函数,在timerCtx类型中,其在符合时间点时,会自动执行cancel()函数。
deadline time.Time
该字段用于存储执行cancel任务的时间点
timeCtx运行步骤
创建:初始化
工作:监听done/等待deadline
删除:执行其内部的cancelCtx的cancel方法 ; 删除与父节点的关系 ;释放time.Timer
创建
初始化
首先,判断其直属父节点的deadline是否早于其自己,若是,则该timerCtx没有创建的必要,直接返回一个cancelCtx的类型(仅用于执行cancel命令)
创建一个新的timerCtx类型,用一个新的cancelCtx和deadline赋值
将timerCtx传播到最近的一个cancelCtx父节点的子节点set下
最后判断比较现在的时间与deadline,若现在晚于deadline,则直接调用其cancel方法,若现在早于deadline,则初始化time.Timer
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
if parent == nil {
panic("cannot create context from nil parent")
}
//首先,判断其直属父节点的deadline是否早于其自己,若是,则该timerCtx没有创建的必要,直接返回一个cancelCtx的类型(仅用于执行cancel命令)
if cur, ok := parent.Deadline(); ok && cur.Before(d) {
// The current deadline is already sooner than the new one.
return WithCancel(parent)
}
//创建一个新的timerCtx类型,用一个新的cancelCtx和deadline赋值
c := &timerCtx{
cancelCtx: newCancelCtx(parent),
deadline: d,
}
//将timerCtx传播到最近的一个cancelCtx父节点的子节点set下
propagateCancel(parent, c)
//最后判断比较现在的时间与deadline,若现在晚于deadline,则直接调用其cancel方法,若现在早于deadline,则初始化time.Timer
dur := time.Until(d)
if dur <= 0 {
c.cancel(true, DeadlineExceeded, nil) // deadline has already passed
return c, func() { c.cancel(false, Canceled, nil) }
}
c.mu.Lock()
defer c.mu.Unlock()
if c.err == nil {
c.timer = time.AfterFunc(dur, func() {
c.cancel(true, DeadlineExceeded, nil)
})
}
return c, func() { c.cancel(true, Canceled, nil) }
}
工作
监听done/等待deadline
利用Done()监听其是否被cancel了(可能是手动调用cancel函数,也有可能是到达时间点)。
调用的是cancelCtx中的Done()来监听。
删除
执行其内部的cancelCtx的cancel方法
想要传递取消信号,利用timerCtx内部的cancelCtx的cancel方法即可,此时,removeFromParent参数选择false,因为其父节点上链接的子节点并不是这个cancelCtx,而是timerCtx。
这是为了防止其父节点删除时,没有删除掉timerCtx,造成程序崩溃。
func (c *timerCtx) cancel(removeFromParent bool, err, cause error) {
c.cancelCtx.cancel(false, err, cause)
//...
}
删除与父节点的关系
func (c *timerCtx) cancel(removeFromParent bool, err, cause error) {
//...
if removeFromParent {
// Remove this timerCtx from its parent cancelCtx's children.
removeChild(c.cancelCtx.Context, c)
}
//...
}
释放time.Timer
若time.Timer被创建,则释放
func (c *timerCtx) cancel(removeFromParent bool, err, cause error) {
//...
c.mu.Lock()
if c.timer != nil {
c.timer.Stop()
c.timer = nil
}
c.mu.Unlock()
}
本次分享结束,本专栏将持续更新有关golang源码的内容,欢迎大家关注
如果想学习更多Context库相关内容:Golang-Context标准库源码深扒-简介&目录