浅析Golang的固定时长定时器和周期性时长定时器

我们之前要想在调度里面实现延时执行,我们可以使用管道阻塞,直到有人往管道里面写东西才变通畅,还可以使用sleep来睡觉,但是睡觉的过程,协程啥也干不了也占用资源。所以我们要用到接下来讲的定时器,不会像sleep那样睡的时候也占用资源。

先来看看下面这段代码:

package main

import (
    "fmt"
    "time"
)

func main() {
    timer := time.NewTimer(3 * time.Second)
    fmt.Println("定时器创建完毕!")
    fmt.Println(time.Now())
    //阻塞3秒后才能读出时间
    x := <- timer.C
    //这个C是一个单向的只读管道
    fmt.Println(x)
}

运行结果是这样的:

定时器创建完毕!
2021-08-24 14:02:28.6664158 +0800 CST m=+0.012997601
2021-08-24 14:02:31.670071 +0800 CST m=+3.016652801

我们可以看到,运行结果和我们要达到的目的基本一致,三秒的定时器创建完毕后,阻塞三秒后才能读出时间。

我们来看看这个:

x := <- timer.C

根据下面这段代码可知,这个C是一个单向的只读管道:

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

如果要描述一个单向的只写的管道,应该这样写:

C chan <- Time

但是如果要达到同样的目的,我们可以使用下面这种更简单的方式:

func main() {
    fmt.Println(time.Now())
    x := <- time.After(3*time.Second)
    fmt.Println(x)
}
time.After()NewTimer(d).CNewTimerTimer.Stop

当然我们也可以使用下面这种方法,两种方法都可以:

x := <- time.NewTimer(3 * time.Second).C

刚才固定时长定时器的就是一个定时炸弹设置为三秒钟那三秒钟之后就爆炸,现在我们看看周期性时长定时器吧!

func main() {
    ticker := time.NewTicker(1 * time.Second)

    var i int
    for{
        x := <- ticker.C
        fmt.Print("\r",x)
        i++
        if i>3{
            //停掉秒表会导致ticker.C永远无法读出数据,
            //一定要读会导致死锁.
            ticker.Stop()
            break
        }
    }
    fmt.Println("计时结束")
}
ticker.Stop()

如果将定时器结束后,你仍然要坚持读,就会出现下面这种情况!

fatal error: all goroutines are asleep - deadlock!

出现死锁!所以这里需要用到break.