关于 select 语句
- select 语句中,case不依赖代码书写顺序。
- 如果case中有1个有消息时,其他case/default则不会执行。
- 如果case中有多个消息时,随机任选1个进行执行,其他不会执行
- 如果所有case都没有消息时,同时含有defalut分支,则会走default分支
- 如果所有case都没有消息时,没有default分支,则会阻塞等待case中返回消息继续执行
func g1(ch chan int) {
ch <- 42
}
func g2(ch chan int) {
ch <- 43
}
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go g1(ch1)
go g2(ch2)
select {
case v1 := <-ch1:
fmt.Println("Got:";, v1)
case v2 := <-ch2:
fmt.Println("Got", v2)
default:
fmt.Println("The default case!")
}
}
由于 goroutine 的执行顺序不确定,所以这里也不确定到底是该输出 42 43 还是 The default case!,可以用下面的脚本执行 5000 次看看(大家把下面的脚本保存成 for.sh,然后 chmod a+x for.sh,接着 ./for.sh)
rm -f for.txt
touch for.txt
for i in `seq 5000`
do
go build -race main.go
./main >> for.txt
rm -f main
done
然后到 for.txt中看输出,大部分都是 default,很小部分是 42 和 43。所以他还是会优先的选择 case 去执行,只是我们不要写这种 select,一旦写了,我们大抵要为这种不确定性付出代价
那么为什么这位兄台的代码一定是 default呢?
func AsyncServices() chan int {
ch := make(chan int, 1)
go func() {
time.Sleep(time.Millisecond * 200) // 不管有没有这个走的都是default
ch <- 1
}()
return ch
}
func TestSelect(t *testing.T) {
select {
case ch := <-AsyncServices():
t.Log(ch)
case <-time.After(time.Millisecond * 100):
t.Error("Time out.")
default:
t.Log("Receive nothing.")
}
}
这个 select 能够确定每个 case 到底怎么执行,大抵上,AsyncServices() 这个函数调用是耗时的,且一定会比 default 慢,如果我们把上述代码改成下面这样子:
这样
func main() {
ch := make(chan int, 1)
go func() {
ch <- 1
}()
select {
case ch := <-ch:
fmt.Println(ch)
case <-time.After(time.Millisecond * 100):
fmt.Println("Time out";)
default:
fmt.Println("Receive nothing")
}
}
或者这样
func Test_ab(t *testing.T) {
ch := make(chan int)
go func() {
ch <- 1
}()
time.Sleep(1000)
select {
case res1 := <-ch:
fmt.Println("case1: ", res1)
//case <-time.After(time.Millisecond * 100):
// fmt.Println("timeout")
//case res2 := <-ch2:
// fmt.Println("case2: ", res2)
default:
fmt.Println("default: ")
}
}
本质上都是在代码进入select 之前, 尽可能让通道返回数据