支持首次触发的 Go Ticker

促使我写这篇文章主要是在写一个关于虚拟货币账户监控的项目时使用 Ticker 的问题。

Ticker 的问题

如果用过 Ticker 的朋友会知道,创建 Ticker 后并不会马上执行,而是会等待一个时间 d,这就是创建时的间隔时间。如果间隔时间很短这基本上不会有太大问题,但是如果对首次执行时间有要求,就会很麻烦。例如以下这个案例:


package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    ts := time.NewTicker(5 * time.Second)
    fmt.Println("start_time#", time.Now().Unix())
    chanClose := make(chan struct{})
    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        defer wg.Done()
        for {
            select {
            case

它将返回以下内容:

start_time# 1656860176
run_time# 1656860181
run_time# 1656860186

为了方便演示我们在事例中设了一个很短的时间,我们可以看到从代码启动到真正定时器触发,代码等待了5秒,就是 time.NewTicker 创建时我们传的参数时间。但如果我们把这个时间改成1个小时,我们需要等待1个小时才会真正开始执行。

寻找解决方案

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    ts := time.NewTicker(5 * time.Second)
    fmt.Println("start_time#", time.Now().Unix())
    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        for ; true;

上述的执行后返回内容:

start_time# 1656860889
run_time# 1656860889
run_time# 1656860894

我们可以看到首次定时器触发任务的时间变成了程序执行的开始时间!在我们的例子中,这种方式没有问题,但是我们需要关注退出条件,在这里是 main goroutine 直接退出。第一个 goroutine 其实直到 main 退出前一直是堵塞状态。如果你的项目中多次使用这种形式的定时器,每一个都会有一个堵塞的 goroutine,虽然不会对你程序造成 panic,但我还是感觉不是很好。

我的版本

先上代码:

package ticktock

import (
    "time"
)
// 这个结构体内容是为了兼容 Ticker 的使用方式
type tickerStart struct {
    C      chan time.Time
    ticker *time.Ticker
    close  chan struct{}
}

func NewTickerStart(d time.Duration) *tickerStart {
    // 这里我们创建的 channel 设了一个 buffer,原因是我们需要
    // 在下面 Start 方法中及时推送当前时间而不至于堵塞。
    //
    c := make(chan time.Time, 1)
    return &tickerStart{ticker: time.NewTicker(d), C: c, close: make(chan struct{})}
}
// 这是我们核心的方法
func (ts *tickerStart) Start() {
    ts.C

使用代码如下:

package main

import (
    "fmt"
    "sync"
    "time"
    "ticktock"
)

func main() {
    fmt.Println("start_time#", time.Now().Unix())
    chanClose := make(chan struct{})
    tts := ticktock.NewTickerStart(5 * time.Second)
    tts.Start()
    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        defer wg.Done()
        for {
            select {
            case

执行返回内容如下:

start_time# 1656861872
run_time# 1656861872
run_time# 1656861877

可以看到,和我们想要的一致。但和官方给出的不同我们不会堵塞 goroutine 。

这是我在写虚拟货币账户监控项目中碰到的其中一个问题,我也会在后续的文章中写一写我碰到的其他问题。当然这个项目会开源,可以关注我的 github GanymedeNil’s github

最后的最后

Original: https://www.cnblogs.com/canyuexiang/p/16441444.html
Author: GanymedeNil
Title: 支持首次触发的 Go Ticker

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/516265/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • strchecker——Go源码字符串规范检查lint工具

    1.背景 在大型项目开发过程中,经常会遇到打印大量日志,输出信息和在源码中写注释的情况。对于软件开发来说,我们一般都是打印输出英文的日志(主要考虑软件在各种环境下的兼容性,如果打印…

    Go语言 2023年5月25日
    057
  • Maglev : A Fast and Reliable Software Network Load Balancer (using Consistent Hashing)

    前言(为什么想读这一篇论文) 这一篇论文吸引我注意的原因是,Consistent Hashing ;本来的特性就是作为分布式缓存之用。谷歌将他们的负载均衡器(代号:Maglev)发…

    Go语言 2023年5月25日
    078
  • 从Go编程看IO多路复用Select

    IO多路复用通过某种机制使进程监听某些文件描述符,当文件描述符中有读或写就绪时,进程能够收到系统内核发送的相应通知从而进行相应的IO操作;IO多路复用有:select、poll、e…

    Go语言 2023年5月25日
    072
  • go泛型教程

    导读: 约束 使用方法 实现原理 跟其它语言的泛型进行对比 用例子学泛型 issues 泛型需满足 go1.18+ go使用interface作为约束,约束的意思是约束了这个泛型都…

    Go语言 2023年5月25日
    078
  • golang的defer踩坑汇总

    变量捕获 defer中的变量会被提前捕获,后续的修改不会影响到已捕获的值,举个例子: 结果defer语句中打印的值是修改前的值。: 最后输出值: 10 Defer运行值: 0 变量…

    Go语言 2023年5月25日
    073
  • Go内存逃逸分析

    Go的内存逃逸及逃逸分析 Go的内存逃逸 分析内存逃逸之前要搞清楚一件事 我们编写的程序中的 函数和 局部变量默认是存放在栈上的(补充一点堆上存储的数据的指针 是存放在栈上的 因为…

    Go语言 2023年5月25日
    065
  • Go代码规范梳理

    注释语句 // Request 表示运行命令的请求。 type Request struct { … // Encode 将 req 的 JSON 编码写入 w 。 func …

    Go语言 2023年5月25日
    081
  • Golang 源码解读 01、深入解析 strings.Builder、strings.Join

    本文从我的《The Go Programming Language》学习笔记中分离出,单独成一篇文章方便查阅参考。 strings.Builder 源码解析 存在意义 使用 (st…

    Go语言 2023年5月25日
    090
  • Go sort包

    sort包简介 官方文档Golang的sort包用来排序,二分查找等操作。本文主要介绍sort包里常用的函数,通过实例代码来快速学会使用sort包 sort包内置函数 sort.I…

    Go语言 2023年5月25日
    0126
  • go语言标准库

    学习go 语言,如果不知道标准库,那很多能力就不知道,标准库应该是程序员可以背下来的 bufio bytes container crypto database debug enc…

    Go语言 2023年5月29日
    071
  • 在开发中将git运用自如

    写这篇文章的初衷是记录自己在开发中使用git遇到的问题和如何利用git进行高效的开发。个人理解来看,很多人对git运用不自如主要是两方面的原因:1、死记硬背命令,这个其实可以通过h…

    Go语言 2023年5月25日
    068
  • sqlx操作MySQL实战及其ORM原理

    sqlx是Golang中的一个知名三方库,其为Go标准库database/sql提供了一组扩展支持。使用它可以方便的在数据行与Golang的结构体、映射和切片之间进行转换,从这个角…

    Go语言 2023年5月25日
    086
  • EbitenCookBook中文教程 第一课:安装 Ebiten

    本文实时更新原址:https://ebitencookbook.vercel.app/docs/CookBook_Start/class1 第一课 安装 Ebiten 欢迎大家来到…

    Go语言 2023年5月25日
    078
  • Go 中的 byte、rune 与 string

    byte 和 rune byte 是 uint8 的别名,其字面量是 8 位整数值,byte 切片相比于不可变的 string 方便常用许多。它可以更改每个字节或字符。这对于处理文…

    Go语言 2023年5月25日
    0178
  • 系统调用跟踪——ls功能实现(二)

    在上篇文章中我们跟踪 ls命令看到了其所使用的这么几个系统调用: stat、openat、fstat、getdents、close、write等,这里再简单介绍下这几个系统调用的功…

    Go语言 2023年5月25日
    083
  • Go语言学习笔记-结构体(Struct)

    Go语言结构体 1、概念结构体是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体。每个值称为结构体的成员。Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不…

    Go语言 2023年5月25日
    072
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球