官方文档及理解
每个case都必须是一个通信
所有channel表达式都会被求值
所有被发送的表达式都会被求值
如果任意某个通信可以进行,它就执行;其他被忽略。
-
如果有多个case都可以运行,Select会随机公平地选出一个执行。其他不会执行。否则:
- 如果有default子句,则执行该语句。
- 如果没有default字句,select将阻塞,直到某个通信可以运行;Go不会重新对channel或值进行求值。
个人的理解及注意事项
如果某个表达式无法求值,不会被阻塞,除非所有表达式都无法求值
-
有多个表达式可以求值,只会执行1个表达式的case且其他表达式相当于没求值一样(测验出来的)
func main() { var ( a = make(chan int, 1) b = make(chan int, 1)//若容量为0,并发函数会阻塞,无法做到同时可以求值 ) go func() { a <- 1 b <- 2 }() for i := 0; i < 1e8; i++ { i = i + i - i }//让并发函数先执行完 var x int select { case x = <-a: println("a") case x = <-b: println("b") } fmt.Println(x) if x == 1 { fmt.Println(<-b) } else { fmt.Println(<-a) } } /* 结果 b 2 1 或 a 1 2 */
-
把信道close了,也可以接收到值,若一个信道close,一个信道没close,都可以求值,有可能出现提前退出
func sel() { a, b := make(chan int, 3), make(chan int) go func() { v, ok, s := 0, false, "" for { select { // case v, ok = <-a: s = "a" case v, ok = <-b: s = "b" } if ok { fmt.Println(s, v) } else { fmt.Println("end") os.Exit(0) } } }() close(a) for i := 0; i < 5; i++ { b <- i } select {} } //结果 b 0 end 或 end
for-select select里面有break 的话只会跳出select