golang-几种定时器的时候

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

a...Z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值