通过创建context上下文关系树,达到控制协程的效果;
golang.org/x/net/context,是golang中的一个标准库,主要作用就是创建一个上下文,对程序中创建的协程通过传递上下文信息来实现对协程的管理
方法以下
context.Background()
context.TODO()
context.WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
context.WithValue(parent Context, key, val any) Context
context.WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
context.WithCancel(parent Context) (ctx Context, cancel CancelFunc)
对于存在若干个协程的程序,协程之前可能会存在如下的关系,这就需要在父协程关闭时,对子协程及时关闭,否则协程可能会持续存在与内存中,造成内存泄漏;
context对子协程的控制销毁就是基于协程创建的过程中,为每个子协程_创建子context_,以WithCancel()方法为例进行分析:
WithCancel()会返回一个新的子context和一个上下文取消方法,当执行cancel时,当前协程下的子context都会被销毁。
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
if parent == nil {
panic("cannot create context from nil parent")
}
c := newCancelCtx(parent)
propagateCancel(parent, &c)
return &c, func() { c.cancel(true, Canceled) }
}
下面时cancelCtx.cancel的源码,可以看到,cacel后,会销毁当前context下的所以child;
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
d, _ := c.done.Load().(chan struct{})
if d == nil {
c.done.Store(closedchan)
} else {
close(d)
}
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)
}
}
其他方法,像WithTimeout()创建的子contex的销毁方法也类似,只是多了timer计时器。
超时器实现
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
handle(ctx)
}
func handle(ctx context.Context) {
result := make(chan string, 1)
go func() {
time.Sleep(10 * time.Second)
result <- "gao"
}()
select {
case <-ctx.Done():
fmt.Println("handle time out")
case <-result:
fmt.Println("working done")
}
}
控制协程退出的方法也类型,只需要在监听到ctx.Done()信号时处理即可
select {
case <-ctx.Done():
return
}
https://juejin.cn/post/7096105924502224904