Go channel系列:html
谁也没法保证某些状况下的select是否会永久阻塞。不少时候都须要设置一下select的超时时间,能够借助time包的After()实现。函数
time.After()的定义以下:指针
func After(d Duration) <-chan Time
After()函数接受一个时长d,而后它After()等待d时长,等待时间到后,将等待完成时所处时间点写入到channel中并返回这个只读channel。code
因此,将该函数赋值给一个变量时,这个变量是一个只读channel,而channel是一个指针类型的数据,因此它是一个指针。htm
看下面的示例:blog
package main import ( "fmt" "time" ) func main() { fmt.Println(time.Now()) a := time.After(1*time.Second) fmt.Println(<-a) fmt.Println(a) }
输出结果:事件
2018-11-20 19:05:11.5440307 +0800 CST m=+0.001994801 2018-11-20 19:05:12.5496378 +0800 CST m=+1.007601901 0xc042052060
若是将After()放进select语句块的一个case中,那么就可让其它的case有必定的时间长度来监听读、写事件,若是在这段时长内其它case尚未有可读、可写事件,这个After()所在case就会结束当前的select,而后终止select(若是select未在循环中)或进入下一轮select(若是select在循环中)。get
如下是一个示例:string
func main() { ch1 := make(chan string) // 激活一个goroutine,但5秒以后才发送数据 go func() { time.Sleep(5 * time.Second) ch1 <- "put value into ch1" }() select { case val := <-ch1: fmt.Println("recv value from ch1:",val) return // 只等待3秒,而后就结束 case <-time.After(3 * time.Second): fmt.Println("3 second over, timeover") } }
运行后,将在大约3秒以后输出:io
3 second over, timeover
上面出现了超时现象,由于新激活的goroutine首先要等待5秒,而后才将数据发送到channel ch1中。可是main goroutine继续运行到select语句块,因为第一个case未知足条件(注意,main goroutine并不会所以而阻塞)。评估第二个case时,将执行time.After()等待3秒,3秒以后读取到该函数返回的通道数据,因而该case知足select的条件,该select由于没有在循环中,因此直接结束,main goroutine也所以而终止。自始至终,新激活的goroutine都没有机会将数据发送到ch1中。
上面有两个注意点:
<-ch1
的评估。若是将上面go func()
函数的睡眠时间改成2秒,则在3秒等待时间内,第一个case的<-ch1
评估知足条件,因而该case被选中,第二个case被无视。
go func() { time.Sleep(1 * time.Second) ch1 <- "put value into ch1" }()
上面使用After(),也保证了select必定会选中某一个case,这时能够省略default块。
注意,After()放在select的内部和放在select的外部是彻底不同的,更助于理解的示例见下面的Tick()。
After(d)是只等待一次d的时长,并在此次等待结束后将当前时间发送到通道。Tick(d)则是间隔地屡次等待,每次等待d时长,并在每次间隔结束的时候将当前时间发送到通道。
由于Tick()也是在等待结束的时候发送数据到通道,因此它的返回值是一个channel,从这个channel中可读取每次等待完时的时间点。
下面是一个Tick()和After()结合的示例:
package main import ( "fmt" "time" ) func main() { select { case <-time.Tick(2 * time.Second): fmt.Println("2 second over:", time.Now().Second()) case <-time.After(7 * time.Second): fmt.Println("5 second over, timeover", time.Now().Second()) return } }
上面的示例,在等待2秒以后,就会由于读取到了time.Tick()的通道数据而终止,由于select并未在循环内。
若是select在循环内,第二个case将永远选择不到。由于每次select轮询中,第一个case都由于2秒而先被选中,使得第二个case的评估老是被中断。进入下一个select轮询后,又会从新开始评估两个case,分别等待2秒和7秒。
func main() { for { select { case <-time.Tick(2 * time.Second): fmt.Println("2 second over:", time.Now().Second()) case <-time.After(7 * time.Second): fmt.Println("5 second over, timeover", time.Now().Second()) return } } }
上面不正常执行的缘由是由于每次select都会从新评估这些表达式。若是把这些表达式放在select外面,则正常:
package main import ( "fmt" "time" ) func main() { tick := time.Tick(1 * time.Second) after := time.After(7 * time.Second) fmt.Println("start second:",time.Now().Second()) for { select { case <-tick: fmt.Println("1 second over:", time.Now().Second()) case <-after: fmt.Println("7 second over:", time.Now().Second()) return } } }
返回:
start second: 9 1 second over: 10 1 second over: 11 1 second over: 12 1 second over: 13 1 second over: 14 1 second over: 15 1 second over: 16 7 second over: 16
将time.Tick()和time.After()放在for...select的外面,使得select每次只评估通道是否可读、可写事件,而不会从新执行time.Tick()和time.After(),使得它们从新进入计时状态。
注意上面的输出结果中,有两行:
1 second over: 16 7 second over: 16
说明在第16秒的时候,两个case都评估为真了,可是这一次选择了第一个case,而后进入下一个select过程,由于select的随机选择性,它会保证全部知足条件的case尽可能均衡分布,此次将选择第二个case,它仍然为第16秒,这时由于一次for和select调用所花的时间不可能会超过1秒而进入第17秒。