面试题
这是Go Quiz系列中对于channel的第2篇,波及channel
被close后的个性,以及在select
和channel
一起应用时的注意事项。
这道题目来源于Google的工程师Valentin Deleplace。
package mainimport "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/...