当前位置: 首页 > article >正文

【Go底层】time包中Timer定时器原理

目录

  • 1、背景
  • 2、go版本
  • 3、源码解释
    • 【1】Timer结构
    • 【2】NewTimer函数解释
    • 【3】After和AfterFunc函数解释
  • 4、Timer定时间隔执行任务
  • 5、总结

1、背景

之前讲过Ticker定时器,每隔一段时间往通道写入当前时间。time包中还提供了另一种定时器:Timer,与Ticker不同的是,Timer只会执行一次,但是也有办法定时间隔执行,下面我们来大概理解一下其实现原理。

2、go版本

$ go version
go version go1.21.4 windows/386

3、源码解释

【1】Timer结构

Timer底层结构如下:

type Timer struct {
	C <-chan Time   //只读的时间通道
	r runtimeTimer  //底层定时结构
}

runtimeTimer结构如下:

type runtimeTimer struct {
	pp       uintptr
	when     int64    //什么时候执行任务
	period   int64    //与Ticker的区别就是没用此字段
	f        func(any, uintptr) 
	arg      any
	seq      uintptr
	nextwhen int64
	status   uint32
}

Timer结构和Ticker一模一样,没什么区别。

【2】NewTimer函数解释

初始化一个Timer对象用的函数是NewTimer,源码如下:

func NewTimer(d Duration) *Timer {
	c := make(chan Time, 1)
	t := &Timer{
		C: c,
		r: runtimeTimer{
			when: when(d),
			f:    sendTime,
			arg:  c,
		},
	}
	startTimer(&t.r)
	return t
}

看的出来与Ticker的唯一区别就是没有赋值period字段,所以不会像Ticker一样每隔一段时间往通道写数据,这就是Timer只会往通道写一次数据的原因。

【3】After和AfterFunc函数解释

TImer定时器我们经常使用的2个函数就是After和AfterFunc,源码如下:

func After(d Duration) <-chan Time {
	return NewTimer(d).C
}

func AfterFunc(d Duration, f func()) *Timer {
	t := &Timer{
		r: runtimeTimer{
			when: when(d),
			f:    goFunc,
			arg:  f,
		},
	}
	startTimer(&t.r)
	return t
}

能看出来After函数就是将TImer中的只读通道作为返回值,我们能直接从这个通道中读取执行一次任务的时间,AfterFunc就是到达指定时间之后执行我们自定义的函数。

4、Timer定时间隔执行任务

利用Timer对象提供的Reset函数就可以实现定时执行任务的功能,示例代码如下:

func main() {
	timer := time.NewTimer(5 * time.Second)

	for {
		t := <-timer.C

		logger.Info("exec task", zap.Time("t", t))

		timer.Reset(5 * time.Second)
	}
}

控制台输出:

[2024-12-01 13:37:36.778] | INFO  | Goroutine:1  | [chan_demo/main.go:110]      | exec task | {"t": "[2024-12-01 13:37:36.778]"}
[2024-12-01 13:37:41.830] | INFO  | Goroutine:1  | [chan_demo/main.go:110]      | exec task | {"t": "[2024-12-01 13:37:41.830]"}
[2024-12-01 13:37:46.833] | INFO  | Goroutine:1  | [chan_demo/main.go:110]      | exec task | {"t": "[2024-12-01 13:37:46.833]"}
[2024-12-01 13:37:51.842] | INFO  | Goroutine:1  | [chan_demo/main.go:110]      | exec task | {"t": "[2024-12-01 13:37:51.842]"}

可以看到每隔5秒执行一次,需要注意的是,在循环里一直调用time.After也能实现定时间隔的任务,但是After函数内部会初始化一个Timer对象,每次循环都会创建一个Timer对象,增加了垃圾回收的开销,所以用Rest函数只用创建一个对象,推荐使用这种方式。

5、总结

通过看Timer的源码之后,能更好的根据不同的业务场景去使用不同的定时器,对于不满足业务场景的定时器,可以采用工厂模式的方式封装一个自定义的定时器去满足我们的业务使用。


http://www.kler.cn/a/421725.html

相关文章:

  • 【分组去重】.NET开源 ORM 框架 SqlSugar 系列
  • 论文阅读:Generating Synthetic Data for Medical Imaging
  • 企业AI助理在数据分析与决策中扮演的角色
  • B树与B+树的区别,为什么MySQL使用B+树不使用B树
  • Elasticsearch在liunx 中单机部署
  • ElasticSearch QueryDSL详解
  • 通过JS逆向,爬取音乐(仅供学习交流,严禁非法使用)
  • Kubernetes集群添加主机名解析
  • Node.js 实战: 爬取百度新闻并序列化 - 完整教程
  • c++预编译头文件
  • java调用ai模型:使用国产通义千问完成基于知识库的问答
  • 详解日志格式配置:XML 与 Spring Boot 配置文件格式
  • [RabbitMQ] 延迟队列+事务+消息分发
  • macOS运行amd64的镜像
  • 李飞飞:Agent AI 多模态交互的前沿探索
  • 为什么同一个网络下的其它电脑无法访问部署在mac上的flask服务
  • 容器化与容器编排(Containerization and Orchestration)
  • 【Azure Cache for Redis】Redis的导出页面无法配置Storage SAS时通过az cli来完成
  • Flutter 中实现WrapContent状态
  • VUE脚手架练习