[Golang]-7 定时器和打点器


我们常常需要在未来某个时刻运行 Go 代码,或者在某段时间间隔内重复运行。
Go 的内置 定时器 和 打点器 特性让这些很容易实现。

定时器

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

通过 time.NewTimer() 创建,这种类型,timer只会执行一次,当然,可以在执行完以后通过调用 timer.Reset() 让定时器再次工作,并可以更改时间间隔。

例子1:

import (
	"fmt"
	"time"
)

func main() {
	// 定时器表示在未来某一时刻的独立事件。
	// 你告诉定时器需要等待的时间,然后它将提供一个用于通知的通道。这里的定时器将等待 2 秒。
	timer1 := time.NewTimer(time.Second * 2)

	// <-timer1.C 直到这个定时器的通道 C 明确的发送了定时器失效的值之前,将一直阻塞。

	<-timer1.C
	fmt.Println("Timer 1 expired")

	// 如果你需要的仅仅是单纯的等待,你需要使用 time.Sleep。
	// 定时器是有用原因之一就是你可以在定时器失效之前,取消这个定时器。这是一个例子
	timer2 := time.NewTimer(time.Second)
	go func() {
		<-timer2.C
		fmt.Println("Timer 2 expired")
	}()

	stop2 := timer2.Stop()
	if stop2 {
		fmt.Println("Timer 2 stopped")
	}
}

输出:

imer 1 expired
Timer 2 stopped

说明:第一个定时器将在程序开始后 ~2s 失效,但是第二个在它没失效之前就停止了。

例子2:

func main() {
	ch := make(chan int)
	// timer 只能按时触发一次,可通过Reset()重置后继续触发。
	timer := time.NewTimer(time.Second * 1)
	go func() {
		var x int
		for {
			select {
			case <-timer.C:
				x++
				fmt.Printf("%d,%s\n", x, time.Now().Format("2006-01-02 15:04:05"))
				if x < 10 {
					timer.Reset(time.Second * 1)
				} else {
					ch <- x
				}
			}
		}
	}()
	fmt.Println(<-ch)
}

输出

1,2020-12-17 09:12:42
2,2020-12-17 09:12:43
3,2020-12-17 09:12:44
4,2020-12-17 09:12:45
5,2020-12-17 09:12:46
6,2020-12-17 09:12:47
7,2020-12-17 09:12:48
8,2020-12-17 09:12:49
9,2020-12-17 09:12:50
10,2020-12-17 09:12:51
10

打点器

定时器 是当你想要在未来某一刻执行一次时使用的
打点器 则是当你想要在固定的时间间隔重复执行准备的。这里是一个打点器的例子,它将定时的执行,直到我们将它停止。

type Ticker struct {
    C <-chan Time // The channel on which the ticks are delivered.
    r runtimeTimer
}

例子1:

func main() {
	// 打点器和定时器的机制有点相似:一个通道用来发送数据。
	// 这里我们在这个通道上使用内置的 range 来迭代值每隔500ms 发送一次的值。
	ticker := time.NewTicker(time.Millisecond * 500)
	go func() {
		for t := range ticker.C {
			fmt.Println("Tick at", t)
		}
	}()

	// 打点器可以和定时器一样被停止。一旦一个打点停止了,将不能再从它的通道中接收到值。我们将在运行后 1600ms停止这个打点器。
	time.Sleep(time.Millisecond * 1600)
	ticker.Stop()
	fmt.Println("Ticker stopped")
}

输出:

Tick at 2020-12-17 09:16:10.9695842 +0800 CST m=+0.505906801
Tick at 2020-12-17 09:16:11.4687539 +0800 CST m=+1.005076501
Tick at 2020-12-17 09:16:11.9687398 +0800 CST m=+1.505062401
Ticker stopped

当我们运行这个程序时,这个打点器会在我们停止它前打点3次。

例子2:

func main() {
	// 打点器和定时器的机制有点相似:使用一个通道用来发送数据。
	ticker := time.NewTicker(time.Second * 1) // 运行时长
	ch := make(chan int)
	go func() {
		var x int
		for x < 10 {
			select {
			case <-ticker.C:
				x++
				fmt.Printf("%d\n", x)
			}
		}
		ticker.Stop()
		ch <- 0
	}()

	// 通过通道阻塞,让任务可以执行完指定的次数。
	<-ch
}

输出:

1
2
3
4
5
6
7
8
9
10

该ticker每1秒触发一次,即ticker.C中每一秒会有一个内容加入,最后通过向ch中写入数字,让程序解除阻塞,继续执行。

After()方法

例子:

func main() {
	// 阻塞一下,等待主进程结束
	tt := time.NewTimer(time.Second * 5)
	<-tt.C
	fmt.Println("over.")

	<-time.After(time.Second * 3)
	fmt.Println("再等待3秒退出。tt 没有终止,打印出 over 后会看见在继续执行...")
	tt.Stop()
	<-time.After(time.Second * 1)
	fmt.Println("tt.Stop()后, tt 仍继续执行,只是关闭了 tt.C 通道。")
}

输出

over.
再等待3秒退出。tt 没有终止,打印出 over 后会看见在继续执行...
tt.Stop()后, tt 仍继续执行,只是关闭了 tt.C 通道。
posted @ 2020-12-17 09:29  哆啦梦乐园  阅读(226)  评论(0编辑  收藏  举报