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(),停止周期性任务。

相关推荐
熏鱼的小迷弟Liu2 小时前
【MySQL】一篇讲透MySQL的MVCC机制!
数据库·mysql
恋猫de小郭3 小时前
Fluttercon EU 2025 :Let‘s go far with Flutter
android·开发语言·flutter·ios·golang
key065 小时前
网络安全等级保护测评实施过程
数据库·安全·web安全·安全合规
程序猿阿伟5 小时前
《政企API网关:安全与性能平衡的转型实践》
网络·数据库·安全
muren5 小时前
Qt DuckDB SQL 驱动插件
数据库
一匹电信狗6 小时前
【MySQL】数据库基础
linux·运维·服务器·数据库·mysql·ubuntu·小程序
LoneEon6 小时前
Ubuntu 部署 ClickHouse:高性能分析型数据库(附shell脚本一键部署↓)
数据库·clickhouse
189228048617 小时前
NX482NX486美光固态闪存NX507NX508
大数据·网络·数据库·人工智能·性能优化
有一个好名字8 小时前
从 3.6 亿订单表到毫秒级查询:分库分表指南
数据库·oracle