這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
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 intfunc (*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。