Go语言的定时器实质是单向通道,time.Timer结构体类型中有一个time.Time类型的单向chan,源码(src/time/time.go)如下
type Timer struct {
C <-chan Time
r runtimeTimer
初始化 Timer 方法为NewTimer
package main
import (
"fmt"
"time"
func main() {
t := time.NewTimer(time.Second * 2)
defer t.Stop()
for {
<-t.C
fmt.Println("timer running...")
// 需要重置Reset 使 t 重新开始计时
t.Reset(time.Second * 2)
输出
timer running…
timer running…
timer running…
timer running…
这里使用NewTimer定时器需要t.Reset重置计数时间才能接着执行。如果注释 t.Reset(time.Second * 2)会导致通道堵塞,报fatal error: all goroutines are asleep - deadlock!错误。
同时需要注意 defer t.Stop()在这里并不会停止定时器。这是因为Stop会停止Timer,停止后,Timer不会再被发送,但是Stop不会关闭通道,防止读取通道发生错误。
t := time.NewTimer(time.Second * 2)
ch := make(chan bool)
go func(t *time.Timer) {
defer t.Stop()
for {
select {
case <-t.C:
fmt.Println("timer running....")
// 需要重置Reset 使 t 重新开始计时
t.Reset(time.Second * 2)
case stop := <-ch:
if stop {
fmt.Println("timer Stop")
return
time.Sleep(10 * time.Second)
ch <- true
close(ch)
time.Sleep(1 * time.Second)
time.After
time.After()表示多长时间长的时候后返回一条time.Time类型的通道消息。但是在取出channel内容之前不阻塞,后续程序可以继续执行。
先看源码(src/time/sleep.go)
func After(d Duration) <-chan Time {
return NewTimer(d).C
}
通过源码我们发现它返回的是一个NewTimer(d).C,其底层是用NewTimer实现的,所以如果考虑到效率低,可以直接自己调用NewTimer。
package main
import (
"fmt"
"time"
func main() {
t := time.After(time.Second * 3)
fmt.Printf("t type=%T\n", t)
//阻塞3秒
fmt.Println("t=", <-t)
基于time.After()特性可以配合select实现计时器
package main
import (
"fmt"
"time"
func main() {
ch1 := make(chan int, 1)
ch1 <- 1
for {
select {
case e1 := <-ch1:
//如果ch1通道成功读取数据,则执行该case处理语句
fmt.Printf("1th case is selected. e1=%v\n", e1)
case <-time.After(time.Second*2):
fmt.Println("Timed out")
select语句阻塞等待最先返回数据的channel`,如ch1通道成功读取数据,则先输出1th case is selected. e1=1,之后每隔2s输出 Timed out。
period int64
f func(interface{}, uintptr)