所谓超时,就是一段时间用户没有做出任何操作,这里需要了解 select
select
-
select 用法与 switch 语言非常类似,由 select 开始一个新的选择块,每个选择条件由 case 语句来描述
-
case 语句里必须是一个 IO 操作
-
select语句中,会按顺序从头至尾评估每一个发送和接收的语句,如果其中的任意一语句可以继续执行(即没有被阻塞),那么就从那些可以执行的语句中任意选择一条来使用
-
如果 case 条件中都是阻塞的,那么 select 就会走 default 语句,没有 default 语句就会处于一直等待状态,直到任意一条 case 条件成立
-
一般不建议写 default,写了话,走 default 的概率太大
-
select 一般与 for 循环搭配使用,
超时操作
-
示例代码
func main() { ch1 := make(chan int) ch2 := make(chan int) go func() { for { <-ch1 fmt.Println("get ch1") } }() go func() { for { <-ch2 fmt.Println("get ch2") } }() for { select { case ch1 <- 1: case ch2 <- 2: case <-time.After(time.Second * 1): fmt.Println("tiem out .................") } } }
func main() { ch1 := make(chan int) ch2 := make(chan int) go func() { for { <-ch1 fmt.Println("get ch1") } }() go func() { for { <-ch2 fmt.Println("get ch2") } }() for { select { case ch1 <- 1: case ch2 <- 2: case <-time.After(time.Microsecond * 50): fmt.Println("tiem out .................") } } }
-
第一段代码最后输出结果要么是
get ch1
,要么是get ch2
,到最后都没看到time out ...
,这是为什么? -
第二段代码最后输出结果是打印多次
time out ....
,这是为什么? -
回过头看一下 select 的定义,其中有一句话是这样的:评估每一条 case 语句,任意选择一条未阻塞的语句执行
-
第一段代码 ch1、ch2 将数据发送到下游管道的时间,是明显短于定时器 1 秒,此时定时器相对于它们是处于阻塞状态的
-
第二段代码 ch1、ch2、tiem 都将会被 select 考虑,因为 ch1、ch2 管道的流动速度不是每次都比定时器的 50 微秒快的
实际运用
-
例如聊天室,当客户端连接到服务器,此时我们就开一个协程,基本代码如上,for 循环里面利用 select 不断监听管道流动,定时器时间为 20 秒
-
20 秒钟内,其他管道都处于阻塞状态,也就是没有客户数据的流入,那么 select 将会执行定时器内的操作,操作大多是 return,结束循环,触发 defer,关闭 conn 连接