常常在http框架裏面看到一個context參數,它是作什麼的呢,先簡單看看它的定義。框架
type Context interface { // Deadline returns the time when work done on behalf of this context // should be canceled. Deadline returns ok==false when no deadline is // set. Successive calls to Deadline return the same results. Deadline() (deadline time.Time, ok bool) // Done returns a channel that's closed when work done on behalf of this // context should be canceled. Done may return nil if this context can // never be canceled. Successive calls to Done return the same value. // // WithCancel arranges for Done to be closed when cancel is called; // WithDeadline arranges for Done to be closed when the deadline // expires; WithTimeout arranges for Done to be closed when the timeout // elapses. // // Done is provided for use in select statements: // // // Stream generates values with DoSomething and sends them to out // // until DoSomething returns an error or ctx.Done is closed. // func Stream(ctx context.Context, out chan<- Value) error { // for { // v, err := DoSomething(ctx) // if err != nil { // return err // } // select { // case <-ctx.Done(): // return ctx.Err() // case out <- v: // } // } // } // // See https://blog.golang.org/pipelines for more examples of how to use // a Done channel for cancelation. Done() <-chan struct{} // Err returns a non-nil error value after Done is closed. Err returns // Canceled if the context was canceled or DeadlineExceeded if the // context's deadline passed. No other values for Err are defined. // After Done is closed, successive calls to Err return the same value. Err() error // Value returns the value associated with this context for key, or nil // if no value is associated with key. Successive calls to Value with // the same key returns the same result. // // Use context values only for request-scoped data that transits // processes and API boundaries, not for passing optional parameters to // functions. // // A key identifies a specific value in a Context. Functions that wish // to store values in Context typically allocate a key in a global // variable then use that key as the argument to context.WithValue and // Context.Value. A key can be any type that supports equality; // packages should define keys as an unexported type to avoid // collisions. // // Packages that define a Context key should provide type-safe accessors // for the values stored using that key: // // // Package user defines a User type that's stored in Contexts. // package user // // import "context" // // // User is the type of value stored in the Contexts. // type User struct {...} // // // key is an unexported type for keys defined in this package. // // This prevents collisions with keys defined in other packages. // type key int // // // userKey is the key for user.User values in Contexts. It is // // unexported; clients use user.NewContext and user.FromContext // // instead of using this key directly. // var userKey key = 0 // // // NewContext returns a new Context that carries value u. // func NewContext(ctx context.Context, u *User) context.Context { // return context.WithValue(ctx, userKey, u) // } // // // FromContext returns the User value stored in ctx, if any. // func FromContext(ctx context.Context) (*User, bool) { // u, ok := ctx.Value(userKey).(*User) // return u, ok // } Value(key interface{}) interface{} }
能夠看到它一個接口類型、主要包含4個成員,咱們暫時不知道它們的意義。ide
Deadline() (deadline time.Time, ok bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{}
package main import ( "context" "fmt" "time" ) func cancel1() { // gen generates integers in a separate goroutine and // sends them to the returned channel. // The callers of gen need to cancel the context once // they are done consuming generated integers not to leak // the internal goroutine started by gen. gen := func(ctx context.Context) <-chan int { dst := make(chan int) n := 1 go func() { for { select { case <-ctx.Done(): fmt.Println("ctx.Done()") return // returning not to leak the goroutine case dst <- n: n++ } } }() return dst } ctx, cancel := context.WithCancel(context.Background()) defer cancel() // cancel when we are finished consuming integers for n := range gen(ctx) { fmt.Println(n) if n == 5 { break } } } func main() { fmt.Println("-----start-----") fmt.Println("call cancel1()") go cancel1() fmt.Println("cancel1() end") time.Sleep(time.Second) fmt.Println("----end------") }
控制檯會輸出
函數
咱們能夠看到,for循環執行了5次 context 的派生函數gen()後,經過cancel()函數退出了gen裏面的協程。this
那麼它在這裏的做用也就清楚了:code
設置截止日期,超時或調用取消函數來通知全部使用任何派生 context 的函數來中止運行並返回。協程
我們這是取消函數對象
withcancel
此函數建立從傳入的父 context 派生的新 context。父 context 能夠是後臺 context 或傳遞給函數的 context。
返回派生 context 和取消函數。只有建立它的函數才能調用取消函數來取消此 context。blog
deadline接口
WithDeadline()返回其父項的派生 context,當截止日期超過或取消函數被調用時,該 context 將被取消。例如,您能夠建立一個將在之後的某個時間自動取消的 context,並在子函數中傳遞它。當由於截止日期耗盡而取消該 context 時,獲此 context 的全部函數都會收到通知去中止運行並返回。
小例:
func deadline1() { d := time.Now().Add(1200 * time.Millisecond) ctx, cancel := context.WithDeadline(context.Background(), d) // Even though ctx will be expired, it is good practice to call its // cancelation function in any case. Failure to do so may keep the // context and its parent alive longer than necessary. defer cancel() select { case <-time.After(1 * time.Second): fmt.Println("overslept") case <-ctx.Done(): fmt.Println(ctx.Err()) } } func main() { fmt.Println("-----start-----") fmt.Println("call deadline1()") go deadline1() fmt.Println("deadline1() end") time.Sleep(3*time.Second) fmt.Println("----end------") }
withtimeout()和WithDeadline()相似,不一樣之處在於它將持續時間做爲參數輸入而不是時間對象。
timeout
// Pass a context with a timeout to tell a blocking function that it // should abandon its work after the timeout elapses. ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) defer cancel() select { case <-time.After(1 * time.Second): fmt.Println("overslept") case <-ctx.Done(): fmt.Println(ctx.Err()) // prints "context deadline exceeded" } Output: context deadline exceeded
如今再看看這context的interface,就能簡單明瞭它們的意義了。
// 返回超時期限。 Deadline() (deadline time.Time, ok bool) // 輔助cancel Done() <-chan struct{} // 獲取返回的錯誤 Err() error Value(key interface{}) interface{}
但是還有個Value, 那咱們再看看。
還有一個withValue() 函數 、
此函數接收 context 並返回派生 context,其中值 val 與 key 關聯,並經過 context 樹與 context 一塊兒傳遞。這意味着一旦得到帶有值的 context,從中派生的任何 context 都會得到此值。不建議使用 context 值傳遞關鍵參數,而是函數應接收簽名中的那些值,使其顯式化。
Code: type favContextKey string f := func(ctx context.Context, k favContextKey) { if v := ctx.Value(k); v != nil { fmt.Println("found value:", v) return } fmt.Println("key not found:", k) } k := favContextKey("language") ctx := context.WithValue(context.Background(), k, "Go") f(ctx, k) f(ctx, favContextKey("color")) Output: found value: Go key not found: color
那麼明顯了 Value(key interface{}) interface{} 返回的便是context關聯的鍵值。