Golang并发编程-context

通过创建context上下文关系树,达到控制协程的效果;

介绍

golang.org/x/net/context,是golang中的一个标准库,主要作用就是创建一个上下文,对程序中创建的协程通过传递上下文信息来实现对协程的管理
方法以下

  1. 创建根context,两个方法没区别,底层实现都是返回空context对象,但因为Background()更好理解,所以比较常用。
context.Background()
context.TODO()
  1. 创建子context,下面方法的作用都是创建一个以parent context为根的子 context。
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)

作用

控制子协程

对于存在若干个协程的程序,协程之前可能会存在如下的关系,这就需要在父协程关闭时,对子协程及时关闭,否则协程可能会持续存在与内存中,造成内存泄漏;
image.png
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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

高冷小伙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值