golang关于关闭管道后从管道中读数据的理解

错误理解

接收操作有两种写法,一种带 “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。可以看到,只有管道已关闭且缓冲区中没有数据时,管道读取表达返回的第二个变量才跟管道关闭状态一致。

你可能感兴趣的:(go,golang,channel)