面试题
这是 Go Quiz 系列中对于 channel 的第 2 篇,波及 channel
被 close 后的个性,以及在 select
和channel
一起应用时的注意事项。
这道题目来源于 Google 的工程师 Valentin Deleplace。
package main
import "fmt"
func main() {data := make(chan int)
shutdown := make(chan int)
close(shutdown)
close(data)
select {
case <-shutdown:
fmt.Print("CLOSED,")
case data <- 1:
fmt.Print("HAS WRITTEN,")
default:
fmt.Print("DEFAULT,")
}
}
- A: 进入 default 分支,打印 ”DEFAULT, “
- B: 进入 shutdown 分支,打印 ”CLOSED, “
- C: 进入 data 分支,打印 ”HAS WRITTEN, “
- D: 程序会 panic
- E: 程序可能 panic,也可能打印 ”CLOSED, “
这道题次要考查以下知识点:
channel
被敞开后,从channel
接收数据和往channel
发送数据会有什么后果?select
的运行机制是怎么的?
解析
- 对于无缓冲区的
channel
,往channel
发送数据和从channel
接收数据都会阻塞。 -
对于
nil channel
和有缓冲区的channel
,收发数据的机制如下表所示:channel nil 空的 非空非满 满了 往 channel 发送数据 阻塞 发送胜利 发送胜利 阻塞 从 channel 接收数据 阻塞 阻塞 接管胜利 接管胜利 敞开 channel panic 敞开胜利 敞开胜利 敞开胜利 -
channel
被敞开后:- 往被敞开的
channel
发送数据会触发 panic。 -
从被敞开的
channel
接收数据,会先读完channel
里的数据。如果数据读完了,持续从channel
读数据会拿到channel
里存储的元素类型的零值。data, ok := <- c
对于下面的代码,如果 channel
c
敞开了,持续从c
里读数据,当c
里还有数据时,data
就是对应读到的值,ok
的值是true
。如果c
的数据曾经读完了,那data
就是零值,ok
的值是false
。 channel
被敞开后,如果再次敞开,会引发 panic。
- 往被敞开的
-
select
的运行机制如下:- 选取一个可执行不阻塞的
case
分支,如果多个case
分支都不阻塞,会随机选一个case
分支执行,和case
分支在代码里写的程序没关系。 - 如果所有
case
分支都阻塞,会进入default
分支执行。 - 如果没有
default
分支,那select
会阻塞,直到有一个case
分支不阻塞。
- 选取一个可执行不阻塞的
依据以上规定,本文最开始的题目,在运行的时候
- data 和 shutdown 这 2 个 channel 都被敞开了。
- 对于敞开的 channel,从 channel 里接收数据,拿到的是 channel 的存储的元素类型的零值,因而
case <-shutdown
这个 case 分支不会阻塞。 - 对于敞开的 channel,向其发送数据会引发 panic,因而
case data <- 1
这个 case 分支不会阻塞,会引发 panic。 - 因而这个 select 语句执行的时候,2 个 case 分支都不会阻塞,都可能执行到。如果执行的是
case <-shutdown
这个 case 分支,会打印 ”CLOSED, “。如果执行的是case data <- 1
这个 case 分支,会导致程序 panic。
因而本题的答案是E
。
加餐
能够回顾 Go quiz 系列中对于 channel 的第一道题目,加深对 channel 的了解。
题目链接地址:channel 面试题和注意事项
开源地址
文章和示例代码开源地址在 GitHub: https://github.com/jincheng9/…
公众号:coding 进阶
集体网站:https://jincheng9.github.io/
知乎:https://www.zhihu.com/people/…
References
- https://twitter.com/val_delep…
- https://github.com/jincheng9/…
- https://github.com/jincheng9/…
- https://github.com/jincheng9/…