golang定时器案例详解

 更新时间:2025年10月20日 17:18:14   作者:小羊在睡觉  
定时器就像一次性闹钟,设置后在指定时间触发一次事件,适合延迟执行或超时控制,本文给大家介绍golang定时器案例讲解,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

一、什么是 Go 语言中的定时器?

基本概念

  • 定时器(Timer):就像一次性闹钟,设置后在指定时间触发一次事件,适合延迟执行或超时控制。
  • 计数器(Ticker):像每小时响一次的钟,定期重复触发事件,适合周期性任务。

Go 语言中的实现

Go 的 time 包提供了两个核心工具:

  • time.Timer:用于一次性定时任务。
  • time.Ticker:用于周期性任务。

二、定时器的产生背景

最原始的问题:程序需要“等待一段时间”

在计算机早期(单线程执行时代),程序的执行是连续的。
但是很多场景需要“延时执行”或“定时触发”:

  • 例 如:
    • 网络请求超时控制(5 秒无响应就退出)
    • 每隔一段时间采集一次数据
    • 定时清理缓存或 session
    • 周期性健康检查(health check)
    • 执行计划任务(类似 cron job)

这些任务都需要一种机制:“在未来的某个时间点执行某个动作”

三、Go 定时器的内部实现原理

Go runtime 的定时器系统是由调度器(scheduler)管理的。
主要基于 最小堆(min-heap)结构

思想:

  • 所有活跃的 TimerTicker 都存储在一个堆中;
  • 堆顶是下一个即将触发的定时器
    • runtime 中有一个专门的 goroutine(叫 timerproc)循环运行:
    • 检查堆顶的到期时间;
    • 睡眠到该时间;
    • 时间一到,就唤醒并执行对应的回调或发送信号。

伪代码示意:

for {
   t := heap.Top() // 最近的定时器
   sleepUntil(t.when)
   trigger(t) // 执行任务
   }

这种机制的好处:

  • 所有定时任务集中管理;
  • 效率极高(对堆的操作,O(log n) 插入、O(1) 取最早任务);

四、time.Timer—— 一次性定时器

1. 基本用法

package main
import (
	"fmt"
	"time"
)
func main() {
	timer := time.NewTimer(2 * time.Second)
	fmt.Println("等待中...")
	<-timer.C // 阻塞,直到2秒后收到信号
	fmt.Println("2秒到了!")
}

解释:

  • time.NewTimer(d) 创建一个定时器;
  • timer.C 是一个只读 channel;
  • 当时间到达后,系统会向 timer.C 发送一个时间值;
  • <-timer.C 会阻塞直到时间到。

2. 另一种方式:time.After

time.After 是一个简化版本,会在指定时间后返回一个 channel:

<-time.After(3 * time.Second) fmt.Println("3秒到了!")

这其实相当于:

timer := time.NewTimer(3 * time.Second) <-timer.C

适合简单一次性延迟的场景。

3.取消定时器:Stop()

如果不再需要一个定时器(例如提前退出、释放资源),应该调用:

timer := time.NewTimer(5 * time.Second)
go func() {
	<-timer.C
	fmt.Println("定时任务执行")
}()
time.Sleep(2 * time.Second)
if timer.Stop() {
	fmt.Println("定时器被取消")
}

 注意:如果定时器已经触发(channel 已经有值),Stop() 会返回 false

4.重置定时器:Reset()

如果想复用一个定时器:

timer := time.NewTimer(2 * time.Second)
<-timer.C // 第一次触发
timer.Reset(1 * time.Second) // 重新开始计时
<-timer.C
fmt.Println("又过了1秒")

五、time.Ticker—— 周期性定时器

1.开启定时器。

ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for t := range ticker.C {
	fmt.Println("时间到了:", t)
}

每隔 1 秒,ticker.C 就会收到一个时间值。

2.停止定时器

必须手动调用:

ticker.Stop()

否则 goroutine 不会结束,可能导致内存泄漏。

六、定时器案例

1.用timer模拟闹钟贪睡

package main
import (
	"fmt"
	"time"
)
func main() {
	// 闹钟,自动重试任务
	// NewTimer 创建一个 Timer,它会在最少过去时间段 d 后到期,向其自身的 C 字段发送当时的时间
	timer1 := time.NewTimer(5 * time.Second)
	fmt.Println("开始时间:", time.Now().Format("2006-01-02 15:04:05"))
	go func(t *time.Timer) {
		times := 0
		for {
			<-t.C
			fmt.Println("timer", time.Now().Format("2006-01-02 15:04:05"))
			// 从t.C中获取数据,此时time.Timer定时器结束。如果想再次调用定时器,只能通过调用 Reset() 函数来执行
			// Reset 使 t 重新开始计时,(本方法返回后再)等待时间段 d 过去后到期。
			// 如果调用时 t 还在等待中会返回真;如果 t已经到期或者被停止了会返回假。
			times++
			// 调用 reset 重发数据到chan C
			fmt.Println("调用 reset 重新设置一次timer定时器,并将时间修改为2秒")
			t.Reset(2 * time.Second)
			if times > 3 {
				fmt.Println("调用 stop 停止定时器")
				t.Stop()
			}
		}
	}(timer1)
	time.Sleep(30 * time.Second)
	fmt.Println("结束时间:", time.Now().Format("2006-01-02 15:04:05"))
	fmt.Println("ok")
}

2.用ticker模拟心跳检测

package main
import (
	"fmt"
	"time"
)
func main() {
	ticker := time.NewTicker(2 * time.Second)
	defer ticker.Stop() // 函数结束时关闭 ticker
	done := make(chan bool)
	// 模拟外部协程:10秒后关闭心跳
	go func() {
		time.Sleep(10 * time.Second)
		done <- true
	}()
	for {
		select {
		case t := <-ticker.C:
			fmt.Println("发送心跳包:", t)
		case <-done:
			fmt.Println("停止发送心跳包")
			return
		}
	}
}

我们可以通过以上案例看到,timer定时器和reset函数组合,也可以达到ticker的效果。

七、总结

  • 对比
    • Timer:一次性触发,适合延迟任务或超时控制。
    • Ticker:周期性触发,适合重复任务。
  • 选择场景
    • Timer:如 HTTP 请求超时、延迟执行任务。
    • Ticker:如定时刷新数据、心跳检测。
  • 注意事项
    • Ticker 如果不停止,会持续运行,可能导致资源泄露。始终记得在不需要时调用 Stop()。
    • Timer 触发后会自动失效,无需担心资源问题。

到此这篇关于golang定时器案例的文章就介绍到这了,更多相关golang定时器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 如何使用Go语言获取当天、昨天、明天、某天0点时间戳以及格式化时间

    如何使用Go语言获取当天、昨天、明天、某天0点时间戳以及格式化时间

    这篇文章主要给大家介绍了关于如何使用Go语言获取当天、昨天、明天、某天0点时间戳以及格式化时间的相关资料,格式化时间戳是将时间戳转换为特定的日期和时间格式,文中通过代码示例介绍的非常详细,需要的朋友可以参考下
    2023-10-10
  • Go语言中的GitOps实战

    Go语言中的GitOps实战

    本文主要介绍了Go语言中的GitOps实战,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2026-04-04
  • golang之数组切片的具体用法

    golang之数组切片的具体用法

    本文主要介绍了golang之数组切片的具体用法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • go time.After优化后性能提升34%内存减少67%

    go time.After优化后性能提升34%内存减少67%

    这篇文章主要介绍了go语言time.After优化后性能提升34%内存减少67%实例分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • Go语言遍历目录的三种方法举例

    Go语言遍历目录的三种方法举例

    学习io之后,尤其是文件操作,我们就可以遍历给定的目录了,这篇文章主要给大家介绍了关于Go语言遍历目录的三种方法,分别是ioutil.ReadDir、filepath.Walk以及filepath.Glob,需要的朋友可以参考下
    2023-11-11
  • Golang实现延迟调用的项目实践

    Golang实现延迟调用的项目实践

    本文主要介绍了Golang实现延迟调用的项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-02-02
  • golang之JWT实现的示例代码

    golang之JWT实现的示例代码

    这篇文章主要介绍了golang之JWT实现的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • Golang中多个if代码优化小技巧

    Golang中多个if代码优化小技巧

    这篇文章主要为大家详细介绍了Golang中一些常用的if代码优化小技巧,w文中的示例代码简洁易懂,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-10-10
  • golang中为什么不存在三元运算符详解

    golang中为什么不存在三元运算符详解

    这篇文章主要给大家介绍了关于golang中为什么不存在三元运算符的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • golang gopm get -g -v 无法获取第三方库的解决方案

    golang gopm get -g -v 无法获取第三方库的解决方案

    这篇文章主要介绍了golang gopm get -g -v 无法获取第三方库的解决方案,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-05-05

最新评论