package main
import (
"fmt"
)
var ch0 = make(chan int)
var ch1 = make(chan int)
var chs = [](chan int){ch0, ch1}
func getCh(i int) chan int{
fmt.Printf("get ch:%d\n", i)
return chs[i]
}
func getNum(i int) int{
fmt.Printf("get num:%d\n", i)
return i
}
func main(){
select {
case getCh(0)<- getNum(0):
fmt.Println("case 0")
case getCh(1)<- getNum(1):
fmt.Println("case 1")
default:
fmt.Println("default")
}
}
上面这段代码输出是什么?
如果你的答案是case 0, case 1随机出现,那么,请接着往下看。
题目的输出是这样的
get ch:0
get num:0
get ch:1
get num:1
default
题目涉及两个知识点:
手册中的说明是这样的:
For all the cases in the statement, the channel operands of receive operations and the channel and right-hand-side expressions of send statements are evaluated exactly once, in source order, upon entering the “select” statement. (更多详情点击这里)
这段话,被好多文章翻译为:
所有channel表达式都会被求值, 所有被发送的表达式都会被求值。求值顺序:自上而下、从左到右。
这…这是欺负我不懂英文么…如此翻译,隐去了太多细节!要想理解这段话,我们用下图来对齐下概念:
需要说明的是,receive operation可以只是<-ch
。
如此一来,这段话就行好理解了。对于select语句中的所有case,图中1,2的ch部分和3的expression部分都会被进行一次求值。求值顺序为代码顺序。
其重点在于,无论相应的case是被选中,求值都会被执行!正如手册中所说
Any side effects in that evaluation will occur irrespective of which (if any) communication operation is selected to proceed.
至此,相应你应该可以明白getCh(), getNum()输出的原因了。
如果确认了解了上面的知识点,我们来看下面的代码,输出是什么?
package main
import (
"fmt"
)
var ch1 = make(chan int)
var ch2 = make(chan int)
func main(){
select {
case ch1 <- <-ch2:
fmt.Println("case 0")
default:
fmt.Println("default")
}
}
答案是死锁了。
原因是这样的<-ch2
被作为发送语句ch1 <- <-ch2
的右值被整体求值。但<-ch2
本身是阻塞状态,无法求值,自然也无法进行select后面的执行步骤,因此死锁。这可能也是手册中所说的求值的副作用之一吧。
如果想解除死锁,简单修改下select部分即可。
select {
case v:= <-ch2:
ch1 <- v
fmt.Println("case 0")
default:
fmt.Println("default")
}
推荐阅读: