错误理解
接收操作有两种写法,一种带 “ok”,反应 channel 是否关闭;一种不带 “ok”
以上的说法来自于《Go程序员面试笔试宝典》的“从 channel 接收数据的过程是怎样的”章节。
其实这是不准确的。返回两个变量的写法的第二个变量,并不直接代表channel是否关闭。以下面这段代码为例:
func main() {
ch := make(chan int, 5)
ch <- 18
close(ch)
x, ok := <-ch
if ok {
fmt.Println("received: ", x)
}
x, ok = <-ch
if !ok {
fmt.Println("channel closed, data invalid.")
}
}
运行结果:
received: 18
channel closed, data invalid.
先创建了一个有缓冲的 channel,向其发送一个元素,然后关闭此 channel。之后两次尝试从 channel 中读取数据,第一次仍然能正常读出值,返回的ok为true,但channel已经关闭。第二次返回的 ok 为 false,说明 channel 已关闭,且通道里没有数据。
正确理解
对channel关闭后,继续读写的正确理解如下:
使用内置函数 close()
可以关闭管道,尝试向已经关闭的管道写入数据会触发panic,但关闭的管道仍可读。
管道读取表达式最多可以给两个变量赋值:
v1 := <-ch
x, ok := <-ch
第一个变量表示独处的数据,第二个变量(bool型)表示是否成功读取了数据,需要注意的是,第二个变量不用于指示管道的关闭状态。
第二个变量常常会被错误地理解成管道的关闭状态,那是因为它的值确实跟管道的关闭状态有关,更确切地说跟管道缓冲区中是否有数据有关。
一个已经关闭的管道有两种情况:
- 管道缓冲区已没有数据;
- 管道缓冲区还有数据。
对于第一种情况,管道已经关闭且缓冲区中没有数据,那么管道读取表达式返回的第一个变量为响应类型的零值,第二个变量为false。
对于第二种情况,管道已经关闭但缓冲区中仍有数据,那么管道读取表达式返回的第一个变量为读取到的数据,第二个变量为true。可以看到,只有管道已关闭且缓冲区中没有数据时,管道读取表达返回的第二个变量才跟管道关闭状态一致。