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。