GO语言中 Channel 有什么特点?读写一个已关闭的 Channel 会发生什么?
GO语言中 Channel 有什么特点?读写一个已关闭的 Channel 会发生什么?
提示
题眼: Channel 有哪些危险操作。
Channel 俗称通道,是 Go 语言中用于协程间通信的重要类型,也是标准库层面支持并发安全使用的类型。它的底层结构体如下:
type hchan struct {
qcount uint // 当前通道中的元素个数
dataqsiz uint // 循环队列的长度
buf unsafe.Pointer // 指向缓冲区的指针
elemsize uint16 // 通道元素的大小
closed uint32 // 是否关闭
elemtype *_type // 通道元素的类型
sendx uint // 发送操作处理到的位置
recvx uint // 接收操作处理到的位置
recvq waitq // 被阻塞的接收操作队列
sendq waitq // 被阻塞的发送操作队列
lock mutex // 互斥锁
}Go 语言中的 Channel 主要有以下几个特点:
- Channel 是类型安全的,发送和接收的数据类型必须一致。
- Channel 天然带有阻塞语义。发送或接收条件不满足时,当前 goroutine 会阻塞。
- Channel 可以分为有缓冲和无缓冲两种。有缓冲通道在缓冲区满时发送会阻塞,在缓冲区空时接收会阻塞。
- Channel 可以关闭。关闭后不能再发送数据,但仍然可以继续接收剩余数据。
读写一个已经关闭的 Channel 时,需要分情况理解:
- 有缓冲 Channel:如果缓冲区里还有数据,接收操作会先把剩余数据读完;等数据全部读完后,再继续读取会立即返回该类型的零值和
false。如果向已关闭的 Channel 写入数据,会直接触发panic。 - 无缓冲 Channel:读取已关闭的无缓冲 Channel,会立即返回该类型的零值和
false;向已关闭的 Channel 写入数据,同样会触发panic。
重要
参考答案:
Channel 是 Go 里专门用来做协程间通信的类型,底层是一个带锁的 hchan 结构。它内部维护了循环队列、发送队列、接收队列以及互斥锁,所以可以在并发场景下安全使用。
它有几个很重要的特点。第一,Channel 是类型安全的,写入和读取的数据类型必须一致。第二,Channel 自带阻塞语义。如果通道里没有数据,读取会阻塞;如果是无缓冲通道,还要求发送和接收两边同时就绪,否则就可能一直阻塞。第三,Channel 可以声明成只读或只写,用来限制函数的使用方式。
通道关闭以后,读取操作要看里面是否还有剩余数据。如果有,就先把剩余数据读完;如果没有了,再继续读取就会返回该类型的零值。标准写法一般是 v, ok := <-ch,其中 ok == false 就表示通道已经关闭并且没有数据了。
需要特别注意的是,向一个已经关闭的通道写数据,或者对同一个通道重复执行 close,都会触发 panic。所以在实际开发里,通常都约定由发送方负责关闭通道。
相关信息
引申: 如何理解:Don’t communicate by sharing memory; share memory by communicating.
