关闭channel
关闭一个channel的底层实现是 runtime.closechan
close(var_chan_1)
//对应汇编
0x0146 00326 (channel.go:51) CALL runtime.closechan(SB)
runtime.closechan
src\runtime\chan.go
- 加锁
channel的关闭过程是加锁的。 发送、读取过程也会加锁。 - 释放等待读取数据的goroutine
将chan上接收队列recvq上的所有goroutine弹出,放在队列glist中。 - 释放等待发送数据的goroutine
将chan上发送队列sendq上的所有goroutine弹出,也放在队列glist中。 - 解锁
释放channel上的锁。 - 唤醒等待读取、发送的goroutine
将glist中的所有goroutine进行唤醒。goready()
注意:
不能重复关闭一个chan,否则会panic。
func closechan(c *hchan) {
if c == nil {
panic(plainError("close of nil channel"))
}
lock(&c.lock)
if c.closed != 0 {
unlock(&c.lock)
panic(plainError("close of closed channel"))
}
if raceenabled {
callerpc := getcallerpc()
racewritepc(c.raceaddr(), callerpc, funcPC(closechan))
racerelease(c.raceaddr())
}
c.closed = 1
var glist gList
// release all readers
for {
sg := c.recvq.dequeue()
if sg == nil {
break
}
if sg.elem != nil {
typedmemclr(c.elemtype, sg.elem)
sg.elem = nil
}
if sg.releasetime != 0 {
sg.releasetime = cputicks()
}
gp := sg.g
gp.param = nil
if raceenabled {
raceacquireg(gp, c.raceaddr())
}
glist.push(gp)
}
// release all writers (they will panic)
for {
sg := c.sendq.dequeue()
if sg == nil {
break
}
sg.elem = nil
if sg.releasetime != 0 {
sg.releasetime = cputicks()
}
gp := sg.g
gp.param = nil
if raceenabled {
raceacquireg(gp, c.raceaddr())
}
glist.push(gp)
}
unlock(&c.lock)
// Ready all Gs now that we've dropped the channel lock.
for !glist.empty() {
gp := glist.pop()
gp.schedlink = 0
goready(gp, 3)
}
}
本文详细解析了Go语言中通道(channel)的关闭过程,包括底层实现、加锁机制、等待goroutine的释放及唤醒流程。强调了通道关闭的不可逆性,避免重复关闭导致的panic。
1763

被折叠的 条评论
为什么被折叠?



