1、前言

在进行并发编程时,有时候会需要定时功能,比如监控某个GO程是否会运行过长时间、定时打印日志等等。

GO标准库中的定时器主要有两种,一种为Timer定时器,一种为Ticker定时器

Timer计时器使用一次后,就失效了,需要Reset()才能再次生效。而Ticker计时器会一直生效,接下来分别对两种进行介绍。

2、Timer定时器

时间到了,执行只执行1次

GO定时器的实现原理:

在一个GO进程中,其中的所有计时器都是由一个运行着 timerproc() 函数的 goroutine 来保护。

它使用时间堆(最小堆)的算法来保护所有的 Timer,其底层的数据结构基于数组的最小堆,堆顶的元素是间隔超时最近的 Timer,这个 goroutine 会定期 wake up,读取堆顶的 Timer,执行对应的 f 函数或者 sendtime()函数(下文会对这两个函数进行介绍),而后将其从堆顶移除。

Timer的结构:

type Timer struct {
    C <-chan Time
    r runtimeTimer
}

Timer中对外暴露的只有一个channel,这个 channel 也是定时器的核心。当计时结束时,Timer会发送值到channel中,外部环境在这个 channel 收到值的时候,就代表计时器超时了,可与select搭配执行一些超时逻辑。

可以通过 time.NewTimertime.AfterFunc 或者 time.After 对一个 Timer进行创建。

示例代码:

func main(){
    // 1.timer基本使用
    //timer1 := time.NewTimer(2 * time.Second)
    //t1 := time.Now()
    //fmt.Printf("t1:%v\n", t1)
    // 无缓冲通道,在没有获取到值时,会阻塞程序
    //t2 := <-timer1.C
    //fmt.Printf("t2:%v\n", t2)


    // 2.验证timer只能响应1次
    //timer2 := time.NewTimer(time.Second)
    //for {
    //    xt2 := <-timer2.C
    //    fmt.Printf("xt2:%v\n", xt2)
    //    fmt.Println("时间到")
    //}

    // 想要再次使用必须使用 Reset 方法重置
    //timer2 := time.NewTimer(time.Second)
    //xt2_1 := <-timer2.C
    //fmt.Printf("xt2_1:%v\n", xt2_1)
    //fmt.Println("时间到")
    //
    //timer2.Reset(time.Second)
    //xt2_2 := <-timer2.C
    //fmt.Printf("xt2_2:%v\n", xt2_2)
    //fmt.Println("时间到")


    // 3.timer实现延时的功能
    // (1)
    //fmt.Printf("Sleep before:%v\n", time.Now())
    //time.Sleep(time.Second)
    //fmt.Printf("Sleep after:%v\n", time.Now())
    // (2)
    //timer3 := time.NewTimer(2 * time.Second)
    //xt3 := <-timer3.C
    //fmt.Printf("xt3:%v\n", xt3)
    //fmt.Println("xt3 ---- 2秒到")
    // (3)
    //xt4 := <-time.After(2*time.Second)
    //fmt.Printf("xt4:%v\n", xt4)
    //fmt.Println("xt4 ---- 2秒到")

    // 4.停止定时器
    //timer4 := time.NewTimer(2 * time.Second)
    //go func() {
    //    <-timer4.C
    //    fmt.Println("定时器执行了")
    //}()
    //b := timer4.Stop()
    //if b {
    //    fmt.Println("timer4已经关闭")
    //}


    // 5.重置定时器
    timer5 := time.NewTimer(3 * time.Second)
    timer5.Reset(1 * time.Second)
    fmt.Println(time.Now())
    fmt.Println(<-timer5.C)

    // 阻塞程序
    for {
    }
}

3、Ticker定时器

时间到了,多次执行

Ticker定时器可以周期性地不断地触发时间事件,不需要额外的Reset操作。

其使用方法与Timer大同小异,也包含 StopReset 等方法。

直接创建的方式有:time.NewTickertime.Tick

func main(){
ticker1 :=time.NewTicker(time.Second)
    go func() {
        for{
            select {
            case t1 := <-ticker1.C:
                fmt.Println("t1 = ", t1)
            }
        }
    }()

    for{
    }
}
作者:joker.liu  创建时间:2023-04-19 15:44
最后编辑:joker.liu  更新时间:2023-04-21 14:33