Go语言并发模型:使用 context

Go语言并发模型:使用 context

介绍

与我们一般编写的http服务器不同, Go为了实现高并发和高性能, 使用了goroutines来处理Conn的读写事件, 这样每个请求都能保持独立,相互不会阻塞,可以高效的响应网络事件。这是Go高效的保证。
而Golang context是Golang应用开发常用的并发控制技术,它与WaitGroup最大的不同点是context对于派生goroutine有更强的控制力,它可以控制多级的goroutine。context翻译成中文是"上下文",即它可以控制一组呈树状结构的goroutine,每个goroutine拥有相同的上下文。
典型的使用场景如下图所示:
在这里插入图片描述
由于goroutine派生出子goroutine,而子goroutine又继续派生新的goroutine,这种情况下使用WaitGroup就不太容易,因为子goroutine个数不容易确定。而使用context就可以很容易实现。

实现原理

context实际上只定义了接口,凡是实现该接口的类都可称为是一种context,官方包中实现了几个常用的context,分别可用于不同的场景。

接口定义

源码包中的接口定义如下所示:

// A Context carries a deadline, cancelation signal, and request-scoped values
// across API boundaries. Its methods are safe for simultaneous use by multiple
// goroutines.
type Context interface {
    // Done returns a channel that is closed when this Context is canceled
    // or times out.
    Done() <-chan struct{}

    // Err indicates why this context was canceled, after the Done channel
    // is closed.
    Err() error

    // Deadline returns the time when this Context will be canceled, if any.
    Deadline() (deadline time.Time, ok bool)

    // Value returns the value associated with key or nil if none.
    Value(key interface{}) interface{}
}
  • Done()
    该方法返回一个channel,需要在select-case语句中使用,如"case <-context.Done():"
    当context关闭后,Done()返回一个被关闭的管道,关闭的管理仍然是可读的,据此goroutine可以收到关闭请求;当context还未关闭时,Done()返回nil
  • Err()
    该方法描述context关闭的原因。关闭原因由context实现控制,不需要用户设置
  • Deadline()
    该方法返回一个deadline和标识是否已设置deadline的bool值,如果没有设置deadline,则ok == false,此时deadline为一个初始值的time.Time值
  • Value()
    有一种context,它不是用于控制呈树状分布的goroutine,而是用于在树状分布的goroutine间传递信息,Value()方法就是用于此种类型的context,该方法根据key值查询map中的value

所有方法

func Background() Context
func TODO() Context

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context

Context是一个接口,想要使用就得实现其方法。在context包内部已经为我们实现好了两个空的Context,可以通过调用Background()和TODO()方法获取。一般的将它们作为Context的根,往下派生。

  • WithCancel()
    WithCancel 以一个新的 Done channel 返回一个父 Context 的拷贝。
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
	c := newCancelCtx(parent)
	    propagateCancel(parent, &c)
	    return &c, func() { c.cancel(true, Canceled) }
}

// newCancelCtx returns an initialized cancelCtx.
func newCancelCtx(parent Context) cancelCtx {
    return cancelCtx{
        Context: parent,
        done:    make(chan struct{}),
    }
}
  • WithDeadline()
    WithDeadline 的最后期限调整为不晚于 d 返回父上下文的副本。如果父母的截止日期已经早于 d,WithDeadline (parent,d) 是在语义上等效为父。返回的上下文完成的通道关闭的最后期限期满后,返回的取消函数调用时,或当父上下文完成的通道关闭,以先发生者为准。
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
	if cur, ok := parent.Deadline(); ok && cur.Before(deadline) {
	    // The current deadline is already sooner than the new one.
	    return WithCancel(parent)
	}
	c := &timerCtx{
	    cancelCtx: newCancelCtx(parent),
	    deadline:  deadline,
	}
	// ...
}
  • WithTimeout()
    WithTimeout 返回 WithDeadline(parent, time.Now().Add(timeout))
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
	return WithDeadline(parent, time.Now().Add(timeout))
}
  • WithValue()
    WithValue 返回的父与键关联的值在 val 的副本。
    使用上下文值仅为过渡进程和 Api 的请求范围的数据,而不是将可选参数传递给函数。
    提供的键必须是可比性和应该不是字符串类型或任何其他内置的类型以避免包使用的上下文之间的碰撞。WithValue 用户应该定义自己的键的类型。为了避免分配分配给接口 {} 时,上下文键经常有具体类型结构 {}。另外,导出的上下文关键变量静态类型应该是一个指针或接口:
func WithValue(parent Context, key, val interface{}) Context {
	if key == nil {
       panic("nil key")
    }
    if !reflect.TypeOf(key).Comparable() {
        panic("key is not comparable")
    }
    return &valueCtx{parent, key, val}
}

总结

Context仅仅是一个接口定义,跟据实现的不同,可以衍生出不同的context类型,主要包括:

  1. cancelCtx实现了Context接口,通过WithCancel()创建cancelCtx实例
  2. timerCtx实现了Context接口,通过WithDeadline()WithTimeout()创建timerCtx实例
  3. valueCtx实现了Context接口,通过WithValue()创建valueCtx实例
    三种context实例可互为父节点,从而可以组合成不同的应用形式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值