Golang-Context标准库源码深扒-timerCtx类型

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标准库源码深扒-简介&目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

动起点

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值