1、time.Sleep
可以直接sleep需要的时间之后,在执行,调度器会把当前协程置为GWaiting状态,放入定时器阻塞堆,是一个小顶堆,不断去堆顶元素
2、time.Timer
简单使用
fmt.Println("now time",time.Now().Format("2006-01-02 15:04:05"))
t := time.NewTimer(3 *time.Second)
<- t.C
fmt.Println("end time",time.Now().Format("2006-01-02 15:04:05"))
//now time 2021-01-27 10:45:54
//end time 2021-01-27 10:45:57
在3s之后输出了时间
实现原理
源码结构体
type Timer struct {
C <-chan Time
r runtimeTimer
}
Timer 由一个只读chan 和运行时时间组成
这个chan是个有缓冲的
在指定时间到达之前,数据没有写入Timer.C,读取的操作这时候会阻塞,到达时间后,写入数据,读取操作读取到数据,继续执行
取消定时器
/
//--> @Description 停止定时器
//--> @Param
//--> @return
func StopTimer(){
fmt.Println("start time: ", time.Now().Format("2006-01-02 15:04:05"))
timer1 := time.NewTimer(3 * time.Second)
go func() {
//协程会阻塞,Timer.C会返回到期的时间
curTime := <-timer1.C
// 定时时间到了之后执行打印操作
fmt.Println("Timer 1 here, current time: ", curTime.Format("2006-01-02 15:04:05"))
}()
timer2 := time.NewTimer(3 * time.Second)
go func() {
<-timer2.C
fmt.Println("Timer 2 here")
}()
// 取消定时器,返回true OR false 代表定时器是否取消成功
stop2 := timer2.Stop()
if stop2 {
fmt.Println("Timer 2 stop")
}
time.Sleep(8 * time.Second)
}
start time: 2021-01-27 11:08:53
Timer 2 stop
Timer 1 here, current time: 2021-01-27 11:08:56
可以看到 ,Timer2 因为取消Stop动作,并没有阻塞协程,还是之间执行了结束
而Timer1 到了定时器的时间之后,执行了输出
重置定时器
可以看到,如果定时器执行完毕再次重置定时器是没有作用的,返回false
可以看到,如果重置定时器是在timer.C之后是没有作用的,返回false
/
//--> @Description 重置定时器,会再次执行一次
//--> @Param
//--> @return
func ResetTimer(){
fmt.Println("start time: ", time.Now().Format("2006-01-02 15:04:05"))
t1 := time.NewTimer(5 * time.Second)
curTime := <-t1.C
fmt.Println("now time: ", curTime.Format("2006-01-02 15:04:05"))
// 重新设置为 3s
ok := t1.Reset(3 * time.Second)
fmt.Println("ok: ", ok)
fmt.Println("now time: ", curTime.Format("2006-01-02 15:04:05"))
}
start time: 2021-01-27 11:14:19
now time: 2021-01-27 11:14:24
ok: false
now time: 2021-01-27 11:14:24
可以看到,如果定时器执行完毕再次重置定时器是没有作用的,返回false
fmt.Println("start time: ", time.Now().Format("2006-01-02 15:04:05"))
t1 := time.NewTimer(5 * time.Second)
curTime := <-t1.C
ok := t1.Reset(3 * time.Second)
// 重新设置为 3s
fmt.Println("ok: ", ok)
fmt.Println("now time: ", curTime.Format("2006-01-02 15:04:05"))
start time: 2021-01-27 11:16:47
ok: false
now time: 2021-01-27 11:16:52
可以看到,如果重置定时器是在timer.C之后是没有作用的,返回false
fmt.Println("start time: ", time.Now().Format("2006-01-02 15:04:05"))
t1 := time.NewTimer(5 * time.Second)
ok := t1.Reset(3 * time.Second)
// 重新设置为 3s
fmt.Println("ok: ", ok)
curTime := <-t1.C
fmt.Println("now time: ", curTime.Format("2006-01-02 15:04:05"))
start time: 2021-01-27 11:20:01
ok: true
now time: 2021-01-27 11:20:04
定时器 重置为3s
设置重试次数和重置时间
timer1 := time.NewTimer(5 * time.Second)
fmt.Println("start time: ", time.Now().Format("2006-01-02 15:04:05"))
go func() {
count := 0
for {
<-timer1.C
fmt.Println("timer 1", time.Now().Format("2006-01-02 15:04:05"))
count++
fmt.Println("调用 Reset() 重新设置过期时间,将时间修改为 2s")
ok := timer1.Reset(2*time.Second)
fmt.Println("ok",ok)
if count > 2 {
fmt.Println("调用 Stop() 停止定时器")
timer1.Stop()
}
}
}()
time.Sleep(15 * time.Second)
fmt.Println("end time:", time.Now().Format("2006-01-02 15:04:05"))
start time: 2021-01-27 11:24:39
timer 1 2021-01-27 11:24:44
调用 Reset() 重新设置过期时间,将时间修改为 2s
ok false
timer 1 2021-01-27 11:24:46
调用 Reset() 重新设置过期时间,将时间修改为 2s
ok false
timer 1 2021-01-27 11:24:48
调用 Reset() 重新设置过期时间,将时间修改为 2s
ok false
调用 Stop() 停止定时器
end time: 2021-01-27 11:24:54
设置执行次数为2,第一次执行之后,将时间设置为了2s,可以设置执行的时间和执行的时间
Ticker
type Ticker struct {
C <-chan Time // The channel on which the ticks are delivered.
r runtimeTimer
}
Ticker 没有reset方法,其实现和Timer一样
简单应用
/
//--> @Description Ticker
//--> @Param
//--> @return
func Ticker(){
fmt.Println(time.Now())
ticker1 := time.NewTicker(2*time.Second)
for {
curTime := <-ticker1.C
fmt.Println(curTime)
}
}
2021-01-27 12:07:45.849139 +0800 CST m=+0.000072214
2021-01-27 12:07:47.853458 +0800 CST m=+2.004303451
2021-01-27 12:07:49.854465 +0800 CST m=+4.005233435
2021-01-27 12:07:51.851467 +0800 CST m=+6.002167200
2021-01-27 12:07:53.84971 +0800 CST m=+8.000348998
每个2s 输出时间 for循环
结合select使用
/
//--> @Description Ticker
//--> @Param
//--> @return
func SelectTicker(){
ticker := time.NewTicker(2 * time.Second)
done := make(chan bool)
go func() {
for {
select {
case <-done:
fmt.Println("done")
return
case t := <-ticker.C:
fmt.Println("Tick at", t)
}
}
}()
time.Sleep(10 * time.Second)
ticker.Stop() // 取消定时器
done <- true
fmt.Println("Ticker stopped")
}
Tick at 2021-01-27 12:18:41.632974 +0800 CST m=+2.005125431
Tick at 2021-01-27 12:18:43.630164 +0800 CST m=+4.002311937
Tick at 2021-01-27 12:18:45.630628 +0800 CST m=+6.002772417
Tick at 2021-01-27 12:18:47.629462 +0800 CST m=+8.001602581
Tick at 2021-01-27 12:18:49.631526 +0800 CST m=+10.003662824
done
Ticker stopped
启动定时器,10s之后停止,输出了5个tick,然后done
不需要清理定时器资源 Tick
定时器资源不清理的话,有可能造成内存溢出,这时候可以使用
Tick
time.After
func After(d Duration) <-chan Time {
return NewTimer(d).C
}
函数的底层也是用了NewTimer的原理
调用该函数不会阻塞当前协程,但如果执行读取操作时,还未达到定时时间,会发生阻塞。
/
//--> @Description
//--> @Param
//--> @return
func After(){
fmt.Println("start time",time.Now().Format("2006-01-02 15:04:05"))
after1 := time.After(2*time.Second)
fmt.Println("读取前, time: ",time.Now().Format("2006-01-02 15:04:05"))
curTime1 := <-after1 // 读取
fmt.Println("读取后 , time: ",time.Now().Format("2006-01-02 15:04:05"))
fmt.Println("读取的内容",curTime1)
fmt.Println()
fmt.Println("start time",time.Now().Format("2006-01-02 15:04:05"))
after2 := time.After(2*time.Second)
time.Sleep(3*time.Second) // 为了使定时时间过期
fmt.Println("读取前, time: ",time.Now().Format("2006-01-02 15:04:05"))
curTime2 := <-after2 // 读取
fmt.Println("读取后 , time: ",time.Now().Format("2006-01-02 15:04:05"))
fmt.Println("读取的内容",curTime2)
}
start time 2021-01-27 13:42:10
读取前, time: 2021-01-27 13:42:10
读取后 , time: 2021-01-27 13:42:12
读取的内容 2021-01-27 13:42:12.101101 +0800 CST m=+2.001771555
start time 2021-01-27 13:42:12
读取前, time: 2021-01-27 13:42:15
读取后 , time: 2021-01-27 13:42:15
读取的内容 2021-01-27 13:42:14.103777 +0800 CST m=+4.004440639
读取的内容是到期的时间
无论sleep多久,到期都会往chan中产生数据
读取会阻塞协程进行
模拟请求超时处理
/
//--> @Description 超时控制
//--> @Param
//--> @return
func TimeoutAfter(){
ch := make(chan string, 1)
go func() {
// 假设我们在这里执行一个外部调用,2秒之后将结果写入 ch
time.Sleep(time.Second * 2)
ch <- "success"
}()
select {
case res := <-ch:
fmt.Println(res)
case <-time.After(time.Second * 3):
fmt.Println("timeout 1")
}
time.Sleep(5 *time.Second)
}
timeout 1
success
当时间为1s的时候,请求时间为2s,过了1s没有收到返回,表示超时
当时间为3s的时候,请求时间为2s,过了1s收到请求返回,正常结果
time.AfterFunc()
函数定义
func AfterFunc(d Duration, f func()) *Timer
time.AfterFunc() 用于延时执行自定义函数,会另起一个协程并等待指定时间过去,然后调用函数 f。它返回一个 Timer,可以通过调用其 Stop() 方法来取消等待和对 f 的调用。
/
//--> @Description
//--> @Param
//--> @return
func AfterFunc(){
ch := make(chan int, 1)
time.AfterFunc(10*time.Second, func() {
fmt.Println("10 seconds over....")
ch <- 8
})
for {
select {
case n := <-ch:
fmt.Println(n, "is arriving")
fmt.Println("Done!")
return
default:
time.Sleep(3 * time.Second)
fmt.Println("time to wait")
}
}
}
time to wait
time to wait
time to wait
10 seconds over....
time to wait
8 is arriving
Done!
创建了10s的定时器和回调函数
取消定时器
/
//--> @Description 取消定时器
//--> @Param
//--> @return
func StopAfterFunc(){
timer1 :=time.AfterFunc(3*time.Second, func() {
fmt.Println("10 seconds over....")
})
time.Sleep(1*time.Second)
timer1.Stop()
fmt.Println("已取消等待和函数执行")
time.Sleep(5*time.Second)
fmt.Println("ok")
}
已取消等待和函数执行
ok
取消定时器 会取消回调函数f的调用
重置定时器
/
//--> @Description 重置定时器
//--> @Param
//--> @return
func StopAfterFunc(){
fmt.Println("start time",time.Now().Format("2006-01-02 15:04:05"))
timer1 :=time.AfterFunc(3*time.Second, func() {
fmt.Println("10 seconds over....")
fmt.Println("run time",time.Now().Format("2006-01-02 15:04:05"))
})
time.Sleep(1*time.Second)
fmt.Println("sleep end time",time.Now().Format("2006-01-02 15:04:05"))
timer1.Reset(6*time.Second)
fmt.Println("reset time",time.Now().Format("2006-01-02 15:04:05"))
time.Sleep(15*time.Second)
}
start time 2021-01-27 14:05:04
sleep end time 2021-01-27 14:05:05
reset time 2021-01-27 14:05:05
10 seconds over....
run time 2021-01-27 14:05:11
重置了定时器后,重新开始计时,然后回调f
死锁
t := time.AfterFunc(8*time.Second, func() {
fmt.Println("Golang")
})
for {
select {
case <-t.C:
fmt.Println("seekload")
break
}
}
Golang
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [select (no cases)]:
main.main()
/Users/zhangsan/go/src/testCode/timer/main.go:63 +0xc8
exit status 2
因为到了定时器出发回调的时候,从chan取数据,此时t。C在取数据,造成了死锁
底层实现原理
https://mp.weixin.qq.com/s?__biz=MzAxMTA4Njc0OQ==&mid=2651440123&idx=3&sn=eb6c9655264e3acc145b80d88d87ef57&chksm=80bb1d09b7cc941ff1cef725b9a5e4c372461c7955edc7422027e7a228b5e6489c6f6e90ba59&scene=21#wechat_redirect