Go语言的context package

Go语言的context package可以把一组用来处理同一请求的函数和goroutine通过context.Context这个类型的变量关联起来,并提供了取消(cancelation)和超时(timeout)机制。个人觉得Sameer Ajmani的这篇文档:Cancelation, Context, and Plumbing写得很清晰,更容易让人理解context package

更新1:下列关于Context的定义选自Go Concurrency Patterns: 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{}
}

四个函数定义如下:
a)Done函数返回一个只读channel,因此对其操作只有close。而这个channel只在Contextcanceltimeout的情况下才会被close
b)Err则是在Done channelclose后,用来获得close的原因,并返回一个non-nil的值:context canceledcontext deadline exceeded
c)Deadline返回Contextcancel的时间。如果okfalse,则表明没有设置deadline
d)Value返回同key绑定的值,如果没有相应的值,则返回nil

更新2:简单地分析一下源码:

// An emptyCtx is never canceled, has no values, and has no deadline.  It is not
// struct{}, since vars of this type must have distinct addresses.
type emptyCtx int

func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
    return
}

func (*emptyCtx) Done() <-chan struct{} {
    return nil
}

func (*emptyCtx) Err() error {
    return nil
}

func (*emptyCtx) Value(key interface{}) interface{} {
    return nil
}

func (e *emptyCtx) String() string {
    switch e {
    case background:
        return "context.Background"
    case todo:
        return "context.TODO"
    }
    return "unknown empty Context"
}

var (
    background = new(emptyCtx)
    todo   = new(emptyCtx)
)

context.Background()context.TODO()所返回的其实都是一个指向emptyCtx类型变量的指针。

再看一下WithCancel()函数实现:

// WithCancel returns a copy of parent with a new Done channel. The returned
// context's Done channel is closed when the returned cancel function is called
// or when the parent context's Done channel is closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
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{}),
    }
}

cancelCtx定义如下:

// A cancelCtx can be canceled.  When canceled, it also cancels any children
// that implement canceler.
type cancelCtx struct {
    Context

    done chan struct{} // closed by the first cancel call.

    mu       sync.Mutex
    children map[canceler]bool // set to nil by the first cancel call
    err      error             // set to non-nil by the first cancel call
}

每次调用WithCancel函数,新生成的Context会包含“父Context”以及一个新的done channel

propagateCancel()函数实现如下:

// propagateCancel arranges for child to be canceled when parent is.
func propagateCancel(parent Context, child canceler) {
    if parent.Done() == nil {
        return // parent is never canceled
    }
    if p, ok := parentCancelCtx(parent); ok {
        p.mu.Lock()
        if p.err != nil {
            // parent has already been canceled
            child.cancel(false, p.err)
        } else {
            if p.children == nil {
                p.children = make(map[canceler]bool)
            }
            p.children[child] = true
        }
        p.mu.Unlock()
    } else {
        go func() {
            select {
            case <-parent.Done():
                child.cancel(false, parent.Err())
            case <-child.Done():
            }
        }()
    }
}

这个函数就是把新生成的Context加到父Contextchildren成员中,这样可以形成“级联”的cancel操作。

其它参考资料:
package context
Concurrent patterns in Golang: Context
Context and Cancellation of goroutines