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
只在Context
被cancel
或timeout
的情况下才会被close
;
b)Err
则是在Done channel
被close
后,用来获得close
的原因,并返回一个non-nil
的值:context canceled
或context deadline exceeded
;
c)Deadline
返回Context
被cancel
的时间。如果ok
为false
,则表明没有设置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
加到父Context
的children
成员中,这样可以形成“级联”的cancel
操作。
其它参考资料:
package context;
Concurrent patterns in Golang: Context;
Context and Cancellation of goroutines。
有疑问加站长微信联系(非本文作者)