使用context实现一对多的goroutine协作
type cancelCtx struct {
Context
mu sync.Mutex // 互斥锁保护下面几个字段
done chan struct{} // 没有初始化, 第一个cancel调用会关闭这个管道
children map[canceler]struct{} // 第一个cancel调用会将其设置为nil
err error // 第一个cancel调用会将其设置为非nil
}
// 返回done
func (c *cancelCtx) Done() <-chan struct{} {
c.mu.Lock()
if c.done == nil {
c.done = make(chan struct{})
}
d := c.done
c.mu.Unlock()
return d
}
// 返回err
func (c *cancelCtx) Err() error {
c.mu.Lock()
defer c.mu.Unlock()
return c.err
}
var closedchan = make(chan struct{})
func init() {
close(closedchan)
}
// 接收一个bool值和一个err值
// 第一次调用,将err值赋值给c.err,同时如果c.done未初始化,就直接赋一个已经关闭的管道给它;初始化了,就关闭
// 对于c.children,都进行取消,最终将c.children设置为nil
// 如果第一个参数removeFromParent是true,那么从父Context中将当前的cancelCtx去掉
// 第二次调用的话,检查到err已经被赋值,就直接返回。
func (c *cancelCtx) cancel(removeFromParent bool, err error) {
if err == nil {
panic("context: internal error: missing cancel error")
}
c.mu.Lock()
if c.err != nil {
c.mu.Unlock()
return // already canceled
}
c.err = err
if c.done == nil {
c.done = closedchan
} else {
close(c.done)
}
for child := range c.children {
// NOTE: acquiring the child's lock while holding parent's lock.
child.cancel(false, err)
}
c.children = nil
c.mu.Unlock()
if removeFromParent {
removeChild(c.Context, c)
}
}