使用 Context 的程式包需要遵循如下的原則來滿足介面的一致性以及便於靜態分析
1.不要把 Context 存在一個結構體當中,顯式地傳入函數。Context 變數需要作為第一個參數使用,一般命名為ctx
2.即使方法允許,也不要傳入一個 nil 的 Context ,如果你不確定你要用什麼 Context 的時候傳一個 context.TODO
3.使用 context 的 Value 相關方法只應該用於在程式和介面中傳遞的和請求相關的中繼資料,不要用它來傳遞一些可選的參數
4.同樣的 Context 可以用來傳遞到不同的 goroutine 中,Context 在多個goroutine 中是安全的
方法說明
- Done 方法在Context被取消或逾時時返回一個close的channel,close的channel可以作為廣播通知,告訴給context相關的函數要停止當前工作然後返回。
當一個父operation啟動一個goroutine用於子operation,這些子operation不能夠取消父operation。下面描述的WithCancel函數提供一種方式可以取消新建立的Context.
Context可以安全的被多個goroutine使用。開發人員可以把一個Context傳遞給任意多個goroutine然後cancel這個context的時候就能夠通知到所有的goroutine。
Err方法返回context為什麼被取消。
Deadline返回context何時會逾時。
- Value返回context相關的資料。
- context 包已經給我們提供了兩個,一個是 Background(),一個是 TODO(),這兩個函數都會返回一個 Context 的執行個體。只是返回的這兩個執行個體都是空 Context。BackGound是所有Context的root,不能夠被cancel。
執行個體1
這個例子傳遞一個上下文,它有一個任意的截止期,它告訴一個阻塞函數,一旦它到達它,它就應該放棄它的工作。
package mainimport ( "context" "fmt" "time")func main() { d := time.Now().Add(50 * 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()) }}
運行結果
context deadline exceeded
執行個體2
package mainimport ( "context" "fmt" "time")func main() { d := time.Now().Add(50 * time.Second) 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()) }}
運行結果
overslept
執行個體3
package mainimport ( "context" "fmt")func main() { // 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(): 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 } }}
運行結果
12345
執行個體4
package mainimport ( "fmt" "context")func main() { 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"))}
運行結果
found value: Gokey not found: color
執行個體5
package mainimport ( "fmt" "context")func main() { f := func(ctx context.Context, k string) { if v := ctx.Value(k); v != nil { fmt.Println("found value:", v) return } fmt.Println("key not found:", k) } ctx := context.WithValue(context.Background(), "language", "Go") f(ctx, "language") f(ctx, "color")}
運行結果
found value: Gokey not found: color