timeAfter()

time.After()

1. time.After()官方解释

刚开始学习时,time包里sleep最常用,在学习context时,突然看到一个time.after,首先是time包的定义:

// After waits for the duration to elapse and then sends the current time
// on the returned channel.
// It is equivalent to NewTimer(d).C.
// The underlying Timer is not recovered by the garbage collector
// until the timer fires. If efficiency is a concern, use NewTimer
// instead and call Timer.Stop if the timer is no longer needed.
func After(d Duration) <-chan Time {
    return NewTimer(d).C
}

英文看不懂没关系,来这里翻译一下,这个软件翻译的太准确了!

2. time.After()代码示例

func main() {
	tchan := time.After(time.Second * 3)
	fmt.Printf("tchan type=%T\n", tchan)
	fmt.Println("mark 1")
	for i := 0; i < 10; i++ {
		fmt.Print(i)
	}
	fmt.Println()
	fmt.Println("tchan,", <-tchan)
	fmt.Println("mark 2")

out

tchan type=<-chan time.Time
mark 1
0123456789
tchan, 2022-08-11 21:47:29.7323266 +0800 CST m=+3.002611601 
mark 2

这个例子比较简单,来分析一下

  • 首先调用time.After(duration),此函数马上返回一个time.Time类型的Chan,不阻塞。注意如果是sleep(),就会阻塞了,就不会往下走了,直到时间到了。而After()不会阻塞,后面你该做什么做什么,不影响。
  • 到了duration时间后,自动塞一个当前时间进去。
  • 写了简单的测试的示例代码,放在了这里。go-basic-exercises/timerAfter_test.go

3. AfterFunc()

在学习了After()后,那么在它的基础上,还有一个AfterFunc(),这个可以指定函数在多长时间执行,也不影响做其他事情。而且可以使用stop() 取消执行。这也是一个很好的设计,比如我们可以设置了一个功能函数,定点到10分钟后执行,但是如果发现发生了异常的事情,或者是发现满足了不需要执行这个函数的条件,就可以stop()掉。就如同抗日片中播放的,计划晚上六点攻打,如果检测到环境不对,那就取消攻打计划。

func TestAfter(t *testing.T) {
	timer := time.AfterFunc(time.Second*30, func() {
		fmt.Println("30s到了哦")
	})
	time.Sleep(time.Second * 20)
	timer.Stop()
	time.Sleep(time.Minute)
}

有意思的是,我在调试程序中,没有设置阻塞时间,程序一下子就运行完成了,如果不设置延时,主goroutine结束了,它也结束了。举一个例子,我设置下午两点的闹钟,这是不是一下子就设置完了,这可以当成函数的声明,只是告诉你到了这个点要做这个事情,但是如果你不延时,阻塞主goroutine,就好像你让手机两点叫你起床,但是你又没等到那个时间把手机关机了,这个时候当然不会执行这个动作了。下面是更形象的例子:

var notify bool

func main() {
	timer := time.AfterFunc(30*time.Millisecond, func() {
		fmt.Println("30ms到了哦,我开始执行啦...")
	})
	fmt.Println("等待到时执行函数,我去忙其他事情...")
	time.Sleep(20 * time.Millisecond)
	notify = true
	if notify {
		timer.Stop()
		fmt.Println("收到通知,情况有变,任务取消")
	}
	time.Sleep(50 * time.Millisecond)
}

out

等待到时执行函数,我去忙其他事情...
收到通知,情况有变,任务取消

4. 总结

  • sleep() 和 after() 底层,应该实现是差不多的,有时间可以分析一下它们源码,看看实现方式的差异。after()在Go中确实是一个很好的设计。sleep() 会阻塞,而after() 不会影响后面的运行。
  • 在异步场景中,尤其是context等,基本上都是返回 channel,然后在一个必要的地方用 select case 来监听。
posted @ 2022-08-29 07:41  sunnybowen  阅读(185)  评论(0编辑  收藏  举报