前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >go语言select语句中的求值问题

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

作者头像
跑马溜溜的球
发布2021-09-23 11:33:09
6330
发布2021-09-23 11:33:09
举报
文章被收录于专栏:日积月累1024

1. 从一个问题说起

代码语言:javascript
复制
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. 答案

题目的输出是这样的

代码语言:javascript
复制
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
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. 更进一步

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

代码语言:javascript
复制
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部分即可。

代码语言:javascript
复制
	select {
    	case v:= <-ch2:
    		ch1 <- v
    		fmt.Println("case 0")
    	default:
    		fmt.Println("default")
	}

推荐阅读:

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/09/14 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 从一个问题说起
  • 2. 答案
  • 3. select语句中的求值
  • 4. 更进一步
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档