GO_并发编程---定时器

前置说明

Go time 包提供两类定时器,底层均基于通道 chan 实现阻塞等待:

  1. Timer一次性定时器,时间到仅触发 1 次;支持手动停止、重置倒计时
  2. Ticker周期性打点器,每隔固定时间自动循环触发;只能手动停止,无 Reset

一、一次性定时器 time.Timer

1. 基础结构

go

Go 复制代码
type Timer struct {
    C <-chan Time // 只读通道,时间到自动写入当前时间
    r runtimeTimer
}

创建语法:

go

Go 复制代码
timer := time.NewTimer(时长)
// 示例:2秒定时器
timer := time.NewTimer(2 * time.Second)

核心特性:定时器到期后,只会向 timer.C 写入一次时间,通道只会有 1 条数据,读完后无新数据,再次读取会永久阻塞死锁。

2. 基础使用:等待定时器到期

写法 1:NewTimer 标准用法

go

Go 复制代码
package main
import (
    "fmt"
    "time"
)

func main() {
    timer := time.NewTimer(2 * time.Second)
    fmt.Println("当前时间:", time.Now())

    // 阻塞读取通道,时间未到则卡住
    t := <-timer.C
    fmt.Println("时间到,触发时间:", t)
}
写法 2:time.After 简化延时(底层封装 NewTimer)

适合只需要一次性延时、不需要停止 / 重置定时器的场景:

go

Go 复制代码
package main
import (
    "fmt"
    "time"
)

func main() {
    // 阻塞2秒
    <-time.After(2 * time.Second)
    fmt.Println("2秒延时结束")
}

注意:time.After 无法手动停止,高频循环中反复调用会大量创建定时器,增加 GC 压力,推荐复用 NewTimer

3. Timer 核心方法 Stop () 停止定时器

作用:倒计时未结束时,终止定时器,不再向通道发送数据返回值 bool

  • true:定时器正常停止(未到期)
  • false:定时器已经到期 / 已停止,停止失败

示例:提前终止定时器,子协程永远阻塞

go

Go 复制代码
package main
import (
    "fmt"
    "time"
)

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

    go func() {
        <-timer.C // 永远读不到数据,阻塞
        fmt.Println("子协程打印")
    }()

    // 直接停止定时器
    ok := timer.Stop()
    fmt.Println("停止结果:", ok)
    for {} // 阻塞主协程观察效果
}

4. Timer 核心方法 Reset (d Duration) 重置倒计时

作用:修改定时器剩余时长,重新开始倒计时返回值规则同 Stop:

  • true:重置前定时器还在运行(未到期)
  • false:定时器已到期 / 已停止

go

Go 复制代码
package main
import (
    "fmt"
    "time"
)

func main() {
    timer := time.NewTimer(3 * time.Second)
    time.Sleep(1 * time.Second)

    // 重置为1秒倒计时
    ok := timer.Reset(1 * time.Second)
    fmt.Println("重置前定时器是否运行:", ok)

    t := <-timer.C
    fmt.Println("重置后触发时间:", t)
}

5. 易错点:Timer 只会触发一次

定时器到期后通道仅写入 1 次数据,循环读取会死锁:

go

Go 复制代码
// 错误示例,会永久阻塞
timer := time.NewTimer(2 * time.Second)
for {
    <-timer.C
    fmt.Println("时间到")
}

如需循环定时,两种方案:

  1. 每次循环调用 timer.Reset()
  2. 改用 Ticker 周期打点器

二、周期打点器 time.Ticker(循环定时)

1. 基础特性

  • 每隔指定时长自动重复触发 ,持续往 ticker.C 写入时间
  • 无 Reset 方法,只能调用 Stop() 彻底关闭循环
  • 不手动 Stop 会持续占用资源,用完必须停止

创建语法:

go

复制代码
ticker := time.NewTicker(间隔时长)

2. 完整示例:循环计数,达到次数停止打点器

go

Go 复制代码
package main
import (
    "fmt"
    "time"
)

func main() {
    // 每秒触发一次
    ticker := time.NewTicker(1 * time.Second)
    i := 0

    for {
        <-ticker.C
        i++
        fmt.Println("计数 i =", i)

        // 执行5次后停止打点器,退出循环
        if i == 5 {
            ticker.Stop()
            break
        }
    }
    fmt.Println("打点器已停止")
}

三、Timer vs Ticker 核心对比总结

表格

特性 time.Timer time.Ticker
触发次数 仅 1 次(一次性) 无限循环周期触发
重置 支持 Reset () 不支持 Reset
停止 Stop (),未到期返回 true Stop (),直接终止循环
适用场景 延时等待、超时控制 定时轮询、周期任务
简化写法 time.After(d) 无简化函数

四、复习速记要点

  1. Timer 是一次性定时器,时间到仅向 C 通道写入一次时间,重复读取会死锁;
  2. time.After(d) 底层是 NewTimer,适合简单延时,无法手动停止;
  3. timer.Stop():提前终止倒计时;timer.Reset():重新修改倒计时时长;
  4. Ticker 循环定时,每隔固定时间自动触发,用完必须调用 Stop 释放资源;
  5. 两者均基于通道阻塞实现等待,读取通道前程序会卡住。
相关推荐
阳光是sunny1 小时前
从链到图:LangGraph 入门基础全解析
前端·人工智能·后端
-银雾鸢尾-1 小时前
C#中的StringBuilder相关方法
开发语言·c#
皮皮林5511 小时前
开源实力派,给本地 AI 装上深度调研能力,一张 3090 跑到 95.7 分!
后端
-银雾鸢尾-2 小时前
C#中结构体与类的区别;抽象类与接口的区别
开发语言·c#
努力的小雨2 小时前
把 Windows 桌面图标做成一个会自己运转的小世界
后端
武子康3 小时前
Search Console Platform Properties 扩大 SEO 资产边界:从 Page Ranking 到 Topic Coverage(5 类误读边界 + 3 表数据层设计)
前端·人工智能·后端
大模型码小白3 小时前
【Python零基础教程】继承、多态与魔法函数:面向对象编程三大核心特性详解
java·大数据·开发语言·人工智能·python·ai编程
腾渊信息科技公司3 小时前
Spring Boot对接MES实战:视觉检测数据自动同步方案
java·人工智能·spring boot·后端·计算机视觉·ai·软件需求
IT_陈寒4 小时前
Vue这个特性差点让我加班到凌晨,谁懂啊
前端·人工智能·后端