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. 两者均基于通道阻塞实现等待,读取通道前程序会卡住。
相关推荐
小徐不徐说2 小时前
序列化与反序列化:把 C++ 对象可靠地保存下来
开发语言·数据库·c++·php
ZhengEnCi2 小时前
模型训练完整流程:从数据准备到模型上线的 10 个关键步骤(全文可跑)
后端·算法
ragnwang2 小时前
Notes: Python pybind11 import C++ module
开发语言·c++·python
王中阳Go2 小时前
后端转 Agent 岗面试怎么答:8 个高频问题与应答口径
人工智能·后端·python·大语言模型
Wang's Blog2 小时前
Java 项目实战: 交友项目 - 搜索附近的功能概述和分析
java·开发语言·交友
程序猿DD2 小时前
OctaFuse Gateway 2.18.0:MCP 接入支持 OAuth,供应商粘性绑定可视化管理
后端
骑着蜗牛撵大象3272 小时前
大模型 Agent 的沙箱护栏:gVisor 隔离、出口白名单与审计闭环
java·开发语言·jvm·安全·agent沙箱·gvisor·microvm
Dxy12393102163 小时前
JS DES 加密详解
开发语言·javascript·arcgis
充值内卷3 小时前
我给桌面加了个鱼缸:C++ 写后端,WebView2 里养一座 Three.js 小岛
开发语言·javascript·c++
云和数据.ChenGuang4 小时前
深入解析 Python 的 dir():从用法到 CPython 源码实现
开发语言·人工智能·python·langchain