select 语句的行为
select就是用来监听和channel有关的IO操作
// https://talks.golang.org/2012/concurrency.slide#32
select {
case v1 := <-c1:
fmt.Printf("received %v from c1\n", v1)
case v2 := <-c2:
fmt.Printf("received %v from c2\n", v1)
case c3 <- 23:
fmt.Printf("sent %v to c3\n", 23)
default:
fmt.Printf("no one was ready to communicate\n")
}
上面这段代码中,select 语句有四个 case 子语句,前两个是 receive 操作,第三个是 send 操作,最后一个是默认操作。代码执行到 select 时,case 语句会按照源代码的顺序被评估,且只评估一次,评估的结果会出现下面这几种情况:
- 除 default 外,如果只有一个 case 语句评估通过,那么就执行这个case里的语句;
- 除 default 外,如果有多个 case 语句评估通过,那么通过伪随机的方式随机选一个;
- 如果 default 外的 case 语句都没有通过评估,那么执行 default 里的语句;
- 如果没有 default,那么 代码块会被阻塞,直到有一个 case 通过评估;否则一直阻塞
- 当case上读取一个通道时,如果这个通道是 nil , 则该case 永远阻塞
- select{} 永远阻塞
select 的使用场景
- 无阻塞的读、写通道。即使通道是带缓存的,也是存在阻塞的情况,使用select可以完美的解决阻塞读写
- 给某个请求/处理/操作,设置超时时间,一旦超时时间内无法完成,则停止处理
- select本色:多通道处理
- done channel
- quit channel
解决阻塞的2种办法
- 使用select的default语句,在channel不可读写时,即可返回
- 使用select+定时器,在超时时间内,channel不可读写,则返回(推荐方式)
// 超时demo
timeout := make (chan bool, 1)
go func() {
time.Sleep(1e9) // sleep one second
timeout <- true
}()
ch := make (chan int)
select {
case <- ch:
case <- timeout:
fmt.Println("timeout!")
}
// 定时器demo
func main() {
tickTimer := time.NewTicker(1 * time.Second)
barTimer := time.NewTicker(60 * time.Second)
for {
select {
case <-tickTimer.C:
fmt.Println("tick")
case <-barTimer.C:
fmt.Println("bar")
}
}
}
当case上读一个通道时,如果这个通道是nil,则该case永远阻塞。这个功能有1个妙用,select通常处理的是多个通道,当某个读通道关闭了,但不想select再继续关注此case,继续处理其他case,把该通道设置为nil即可。
下面是一个合并程序等待两个输入通道都关闭后才退出的例子,就使用了这个特性。
func combine(inCh1, inCh2 <-chan int) <-chan int {
// 输出通道
out := make(chan int)
// 启动协程合并数据
go func() {
defer close(out)
for {
select {
case x, open := <-inCh1:
if !open {
inCh1 = nil
continue
}
out<-x
case x, open := <-inCh2:
if !open {
inCh2 = nil
continue
}
out<-x
}
// 当ch1和ch2都关闭是才退出
if inCh1 == nil && inCh2 == nil {
break
}
}
}()
return out
}
https://mp.weixin.qq.com/s/rXJFAWY-WRp6z5mkBGJtGg
https://segmentfault.com/a/1190000017410112
https://xuchao918.github.io/2019/05/17/Go%E5%B9%B6%E5%8F%91-%E4%BD%BF%E7%94%A8%E5%8D%8F%E7%A8%8B%E3%80%81%E9%80%9A%E9%81%93%E5%92%8Cselect/