go中的Ticker

周期执行time.Ticker

time.Ticker 的功能

time.Ticker 通过一个通道(Ticker.C)每隔固定时间间隔发送时间信号。

它允许用户在指定的时间间隔内执行某些任务,而无需手动管理定时器的触发。

go 复制代码
type Ticker struct {
    C <-chan Time  // 定时器通道
    Stop chan struct{}  // 停止信号通道
    period Duration  // 间隔时间
}
C: Ticker 的核心部分是通道 C,它每当时间到达指定间隔时发送一个时间点(time.Time)。

Stop: Stop 是一个用于停止定时器的通道,调用 Stop() 方法时会关闭这个通道,从而终止定时器的事件发送。

period: period 是定时器的时间间隔,表示触发事件的频率。
  1. Ticker 的工作原理

Ticker 的核心是通过一个后台的协程不断触发事件。其工作方式大致如下:

定时器启动: 当我们调用 time.NewTicker(d) 创建一个定时器时,Go 会创建一个新的协程,它会每隔 d 的时间间隔将当前时间发送到 Ticker.C 通道中。

事件触发: 每次时间间隔到达时,后台的协程会向 Ticker.C 通道发送一个当前的时间 time.Time 对象,允许主程序或其他协程从通道中读取事件。

停止定时器: 当调用 Ticker.Stop() 时,后台的协程会停止发送事件,同时关闭 Ticker.C 通道。

这种基于协程和通道的设计模式保证了定时任务的高效和线程安全。

使用 time.Ticker 实现周期性任务

go 复制代码
func main() {
    ticker := time.NewTicker(2 * time.Second) // 每2秒触发一次
    defer ticker.Stop()                       // 程序结束时停止 ticker
    
    for {
        select {
            case t := <-ticker.C: // 从 ticker 的 channel 接收时间
            fmt.Println("Current time:", t)
        }
    }
}

time.NewTicker(2 * time.Second) 创建了一个新的定时器,它会每隔 2 秒触发一次。

ticker.C 是一个 channel,select 语句可以从中读取到事件。

在 select 语句中读取 ticker.C 时,程序会等待并处理每次触发的事件。
	
Current time: 2025-10-04 02:23:36.3368219 +0800 CST m=+2.000000001
Current time: 2025-10-04 02:23:38.3368219 +0800 CST m=+4.000000001
Current time: 2025-10-04 02:23:40.3368219 +0800 CST m=+6.000000001
Current time: 2025-10-04 02:23:42.3368219 +0800 CST m=+8.000000001
Current time: 2025-10-04 02:23:44.3368219 +0800 CST m=+10.000000001
Current time: 2025-10-04 02:23:46.3368219 +0800 CST m=+12.000000001
Current time: 2025-10-04 02:23:48.3368219 +0800 CST m=+14.000000001
Current time: 2025-10-04 02:23:50.3368219 +0800 CST m=+16.000000001

处理超时或取消周期性任务

如果需要在特定条件下停止周期性任务,可以使用 context 包结合 time.Ticker 或协程来实现超时或取消操作。

在 10 秒后停止周期性任务

go 复制代码
package main

import (
    "context"
    "fmt"
    "time"
)

func periodicTask(ctx context.Context) {
    ticker := time.NewTicker(2 * time.Second) // 每2秒触发一次
    defer ticker.Stop()

    for {
        select {
        case <-ctx.Done(): // 当上下文被取消时退出循环
            fmt.Println("Periodic task stopped.")
            return
        case t := <-ticker.C:
            fmt.Println("Current time:", t)
        }
    }
}

func main() {
    // 创建一个带有超时的上下文
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel() // 确保超时后取消上下文

    go periodicTask(ctx) // 在独立的协程中执行周期性任务

    // 等待上下文超时
    time.Sleep(12 * time.Second) // 程序运行一段时间后退出
}

context.WithTimeout() 创建了一个带有超时的上下文,ctx.Done() 在超时后会被关闭。

periodicTask 函数接收上下文 ctx,并在每次周期性执行时检查 ctx.Done() 是否关闭。

一旦超时,ctx.Done() 会被触发,periodicTask 中的循环退出,任务停止。

结合 time.Ticker 和 context 控制周期性任务的停止

在实际应用中,可能需要在某些条件下停止周期性任务,这时可以通过 context 来传递取消信号。

go 复制代码
package main

import (
    "context"
    "fmt"
    "time"
)

func periodicTask(ctx context.Context) {
    ticker := time.NewTicker(2 * time.Second)
    defer ticker.Stop()

    for {
        select {
        case <-ctx.Done():
            fmt.Println("Periodic task stopped due to context cancellation.")
            return
        case t := <-ticker.C:
            fmt.Println("Current time:", t)
        }
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    go periodicTask(ctx) // 启动周期性任务

    // 模拟一段时间后停止任务
    time.Sleep(6 * time.Second)
    cancel() // 取消上下文,停止周期性任务

    // 等待任务结束
    time.Sleep(2 * time.Second)
}

context.WithCancel 创建了一个可以手动取消的上下文。

调用 cancel() 可以取消上下文,触发 ctx.Done(),停止周期性任务。

相关推荐
倔强的石头_1 天前
kingbase备份与恢复实战(二)—— sys_dump库级逻辑备份与恢复(Windows详细步骤)
数据库
jiayou642 天前
KingbaseES 实战:深度解析数据库对象访问权限管理
数据库
李广坤3 天前
MySQL 大表字段变更实践(改名 + 改类型 + 改长度)
数据库
爱可生开源社区4 天前
2026 年,优秀的 DBA 需要具备哪些素质?
数据库·人工智能·dba
随逸1774 天前
《从零搭建NestJS项目》
数据库·typescript
花酒锄作田5 天前
Gin 框架中的规范响应格式设计与实现
golang·gin
加号35 天前
windows系统下mysql多源数据库同步部署
数据库·windows·mysql
シ風箏5 天前
MySQL【部署 04】Docker部署 MySQL8.0.32 版本(网盘镜像及启动命令分享)
数据库·mysql·docker
李慕婉学姐5 天前
Springboot智慧社区系统设计与开发6n99s526(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端
百锦再5 天前
Django实现接口token检测的实现方案
数据库·python·django·sqlite·flask·fastapi·pip