go关闭的chan是否会阻塞
如果在一个 goroutine 中关闭了 channel,另一个协程对 channel 的读取操作是否会被阻塞?
首先,通过代码验证是否会阻塞。验证代码如下:
func main() {
ch := make(chan struct{})
go func() {
log.Printf("before closing")
close(ch)
log.Printf("after closing")
}()
<-ch
log.Printf("after receiving")
}
输出如下:
2022/07/02 15:36:07 before closing
2022/07/02 15:36:07 after closing
2022/07/02 15:36:07 after receiving
结果分析:
如果ch
不管close
与否都会被阻塞,则 gorountine 中close
之后,receiving
日志也不会进行打印,而是会一直等待ch
发送数据,与实验结果不符。
go 源码中,对 chan 定义如下(runtime.hchan
):
type hchan struct {
qcount uint // total data in the queue
dataqsiz uint // size of the circular queue
buf unsafe.Pointer // points to an array of dataqsiz elements
elemsize uint16
closed uint32
elemtype *_type // element type
sendx uint // send index
recvx uint // receive index
recvq waitq // list of recv waiters
sendq waitq // list of send waiters
// lock protects all fields in hchan, as well as several
// fields in sudogs blocked on this channel.
//
// Do not change another G's status while holding this lock
// (in particular, do not ready a G), as this can deadlock
// with stack shrinking.
lock mutex
}
runtime/chan.go
文件中,当 chan 接受信息时,使用的函数(func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool)
)中,有对closed
标志位的判断,如下:
func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) {
if c == nil {
if !block {
return
}
gopark(nil, nil, waitReasonChanReceiveNilChan, traceEvGoStop, 2)
throw("unreachable")
}
lock(&c.lock)
if c.closed != 0 && c.qcount == 0 {
unlock(&c.lock)
if ep != nil {
typedmemclr(c.elemtype, ep)
}
return true, false
}
由此可见,close
后的 chan,读写操作是不会被阻塞的。