go语言select语句中的求值问题

1. 从一个问题说起

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随机出现,那么,请接着往下看。

2. 答案

题目的输出是这样的

get ch:0
get num:0
get ch:1
get num:1
default

题目涉及两个知识点:

  1. 对于无缓冲的channel,如果接收方未准备好,则发送操作将会被阻塞。因此上面的代码才会走到default分支。
  2. select中,所有case中的语句会被求值。这也是为什么明明走到了default,但getCh(0), getCh(1), getNum(0), getNum(1), 都会被执行。下一小节中我们会着重阐述这个问题。

3. select语句中的求值

手册中的说明是这样的:

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表达式都会被求值, 所有被发送的表达式都会被求值。求值顺序:自上而下、从左到右。

这…这是欺负我不懂英文么…如此翻译,隐去了太多细节!要想理解这段话,我们用下图来对齐下概念:
image

需要说明的是,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()输出的原因了。

4. 更进一步

如果确认了解了上面的知识点,我们来看下面的代码,输出是什么?

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")
	}

推荐阅读:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值