Kubernetes
Kubernetes
关闭某个 channel,会执行函数 closechan
:
1func closechan(c *hchan) {
2 // 关闭一个 nil channel,panic
3 if c == nil {
4 panic(plainError("close of nil channel"))
5 }
6
7 // 上锁
8 lock(&c.lock)
9 // 如果 channel 已经关闭
10 if c.closed != 0 {
11 unlock(&c.lock)
12 // panic
13 panic(plainError("close of closed channel"))
14 }
15
16 // …………
17
18 // 修改关闭状态
19 c.closed = 1
20
21 var glist *g
22
23 // 将 channel 所有等待接收队列的里 sudog 释放
24 for {
25 // 从接收队列里出队一个 sudog
26 sg := c.recvq.dequeue()
27 // 出队完毕,跳出循环
28 if sg == nil {
29 break
30 }
31
32 // 如果 elem 不为空,说明此 receiver 未忽略接收数据
33 // 给它赋一个相应类型的零值
34 if sg.elem != nil {
35 typedmemclr(c.elemtype, sg.elem)
36 sg.elem = nil
37 }
38 if sg.releasetime != 0 {
39 sg.releasetime = cputicks()
40 }
41 // 取出 goroutine
42 gp := sg.g
43 gp.param = nil
44 if raceenabled {
45 raceacquireg(gp, unsafe.Pointer(c))
46 }
47 // 相连,形成链表
48 gp.schedlink.set(glist)
49 glist = gp
50 }
51
52 // 将 channel 等待发送队列里的 sudog 释放
53 // 如果存在,这些 goroutine 将会 panic
54 for {
55 // 从发送队列里出队一个 sudog
56 sg := c.sendq.dequeue()
57 if sg == nil {
58 break
59 }
60
61 // 发送者会 panic
62 sg.elem = nil
63 if sg.releasetime != 0 {
64 sg.releasetime = cputicks()
65 }
66 gp := sg.g
67 gp.param = nil
68 if raceenabled {
69 raceacquireg(gp, unsafe.Pointer(c))
70 }
71 // 形成链表
72 gp.schedlink.set(glist)
73 glist = gp
74 }
75 // 解锁
76 unlock(&c.lock)
77
78 // Ready all Gs now that we've dropped the channel lock.
79 // 遍历链表
80 for glist != nil {
81 // 取最后一个
82 gp := glist
83 // 向前走一步,下一个唤醒的 g
84 glist = glist.schedlink.ptr()
85 gp.schedlink = 0
86 // 唤醒相应 goroutine
87 goready(gp, 3)
88 }
89}
close 逻辑比较简单,对于一个 channel,recvq 和 sendq 中分别保存了阻塞的发送者和接收者。关闭 channel 后,对于等待接收者而言,会收到一个相应类型的零值。对于等待发送者,会直接 panic。所以,在不了解 channel 还有没有接收者的情况下,不能贸然关闭 channel。
close 函数先上一把大锁,接着把所有挂在这个 channel 上的 sender 和 receiver 全都连成一个 sudog 链表,再解锁。最后,再将所有的 sudog 全都唤醒。
唤醒之后,该干嘛干嘛。sender 会继续执行 chansend 函数里 goparkunlock 函数之后的代码,很不幸,检测到 channel 已经关闭了,panic。receiver 则比较幸运,进行一些扫尾工作后,返回。这里,selected 返回 true,而返回值 received 则要根据 channel 是否关闭,返回不同的值。如果 channel 关闭,received 为 false,否则为 true。这我们分析的这种情况下,received 返回 false。