Context一般被譯做上下文,是一個比較抽象的概念。通常理解爲程序單元的一個運行狀態、現場、快照,而翻譯中上下文又很好地詮釋了其本質。 html
每一個Goroutine在執行前,都要先知道程序當前的執行狀態,一般將這些執行狀態封裝在一個Context變量中,傳遞給要執行的Goroutine中。上下文則幾乎已經成爲傳遞與請求同生存週期變量的標準方法。在網絡變成下,當接收到一個網絡請求Request,處理Request時,咱們可能須要開啓不一樣的Goroutine來獲取數據和執行程序邏輯,即一個請求Request,會在多個Goroutine中處理。而這些Goroutine可能須要共享Request的一些信息;同時當Request被取消或者超時的時候,全部從這個Request建立的全部Goroutine也應該被結束。golang
Go的標準庫[golang.org/x/net/context] web
context經常使用的使用姿式:
1.web編程中一個請求對應多個Goroutine之間的數據交互
2.超時控制
3.上下文控制 編程
context的底層結構安全
type Context interface { Deadline() (deadline time.Time, ok bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{} }
庫裏提供了4個Context實現網絡
# 徹底空的Context,實現的函數也都是返回nil,僅僅只是實現了Context的接口 type emptyCtx int # 繼承自Context,同時也實現了canceler接口 type cancelCtx struct { Context mu sync.Mutex done chan struct{} children map[canceler]struct{} err error } # 繼承自**cancelCtx**,增長了timeout機制 type timerCtx struct { cancelCtx timer \*time.Timer // Under cancelCtx.mu. deadline time.Time } # 存儲鍵值對的數據 type valueCtx struct { Context key, val interface{} }
爲了更方便的建立Context,包裏面定義了Background 來做爲全部Context的根,它是一個emptyCtx的實例。session
var ( background = new(emptyCtx) todo = new(emptyCtx) // ) func Background() Context { return background }
你能夠認爲全部的Context是樹的結構,Background是樹的根,當任一Context被取消的時候,那麼繼承它的Context 都將被回收。函數
實現源碼:.net
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { c := newCancelCtx(parent) propagateCancel(parent, &c) return &c, func() { c.cancel(true, Canceled) } }
實戰場景:
執行一段代碼,控制執行到某個度的時候,整個程序結束。翻譯
吃漢堡比賽,奧特曼每秒吃0-5個,計算吃到10的用時
實戰代碼
func main() { ctx, cancel := context.WithCancel(context.Background()) eatNum := chiHanBao(ctx) for n := range eatNum { if n >= 10 { cancel() break } } fmt.Println("正在統計結果。。。") time.Sleep(1 * time.Second) } func chiHanBao(ctx context.Context) <-chan int { c := make(chan int) // 個數 n := 0 // 時間 t := 0 go func() { for { //time.Sleep(time.Second) select { case <-ctx.Done(): fmt.Printf("耗時 %d 秒,吃了 %d 個漢堡 \n", t, n) return case c <- n: incr := rand.Intn(5) n += incr if n >= 10 { n = 10 } t++ fmt.Printf("我吃了 %d 個漢堡\n", n) } } }() return c }
輸出
我吃了 1 個漢堡 我吃了 3 個漢堡 我吃了 5 個漢堡 我吃了 9 個漢堡 我吃了 10 個漢堡 正在統計結果。。。 耗時 6 秒,吃了 10 個漢堡
實現源碼
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) { if cur, ok := parent.Deadline(); ok && cur.Before(d) { // The current deadline is already sooner than the new one. return WithCancel(parent) } c := &timerCtx{ cancelCtx: newCancelCtx(parent), deadline: d, } propagateCancel(parent, c) dur := time.Until(d) if dur <= 0 { c.cancel(true, DeadlineExceeded) // deadline has already passed return c, func() { c.cancel(true, Canceled) } } c.mu.Lock() defer c.mu.Unlock() if c.err == nil { c.timer = time.AfterFunc(dur, func() { c.cancel(true, DeadlineExceeded) }) } return c, func() { c.cancel(true, Canceled) } } func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { return WithDeadline(parent, time.Now().Add(timeout)) }
實戰場景:
執行一段代碼,控制執行到某個時間的時候,整個程序結束。
吃漢堡比賽,奧特曼每秒吃0-5個,用時10秒,能夠吃多少個
實戰代碼:
func main(){ // ctx, cancel := context.WidhDeadline(context.Background(), time.Now().Add(10)) ctx, cancel := contet.WidhTimeout(context.Background(), 10*time.Second) chiHanBao(ctx) defer cancel() } func chiHanBao(ctx context.Context) { n := 0 for { select { case <- ctx.Done(): fmt.Println("stop \n") return default: incr := rand.Intn(5) n += incr fmt.Printf("我吃了 %d 個漢堡\n", n) } time.Sleep(time.Second) } }
輸出:
我吃了 1 個漢堡 我吃了 3 個漢堡 我吃了 5 個漢堡 我吃了 9 個漢堡 我吃了 10 個漢堡 我吃了 13 個漢堡 我吃了 13 個漢堡 我吃了 13 個漢堡 我吃了 14 個漢堡 我吃了 14 個漢堡 stop
實現源碼:
func WidhValue(parent Context, key, value interface{}) Context { if key == nil { panic("nil key") } if !reflect.TypeOf(key).Comparable() { //必須是可比較類型 panic("key is not comparable") } return &valueCtx{parent, key, value} }
實戰場景:
攜帶關鍵信息,爲全鏈路提供線索,好比接入elk等系統,須要來一個trace_id,那WithValue就很是適合作這個事。
實戰代碼:
func main() { ctx := context.WithValue(context.Background(), "trace_id", "88888888") // 攜帶session到後面的程序中去 ctx = context.WithValue(ctx, "session", 1) process(ctx) } func process(ctx context.Context) { session, ok := ctx.Value("session").(int) fmt.Println(ok) if !ok { fmt.Println("something wrong") return } if session != 1 { fmt.Println("session 未經過") return } traceID := ctx.Value("trace_id").(string) fmt.Println("traceID:", traceID, "-session:", session) }
輸出:
traceID: 88888888 -session: 1
https://blog.csdn.net/u011957...
https://www.cnblogs.com/zhang...