上下文 context.Context 一般用於在 API 邊界之間以及過程之間傳遞截止時間、取消訊號或者其他請求相關的資料。
一個介面
context.Context 是 Go 語言在 1.7 版本中引入標準庫的介面,該介面定義了四個需要實現的方法,其中包括:
Deadline
— 返回 context.Context 被取消的時間,也就是完成工作的截止日期;Done
— 返回一個 Channel,這個 Channel 會在當前工作完成或者上下文被取消之後關閉,多次呼叫 Done 方法會返回同一個 Channel;Err
— 返回 context.Context 結束的原因,它只會在 Done 返回的 Channel 被關閉時才會返回非空的值;- 如果 context.Context 被取消,會返回 Canceled 錯誤;
- 如果 context.Context 超時,會返回 DeadlineExceeded 錯誤;
Value
— 從 context.Context 中獲取鍵對應的值,對於同一個上下文來說,多次呼叫 Value 並傳入相同的 Key 會返回相同的結果,該方法可以用來傳遞請求特定的資料;
四個具體實現
emptyCtx
本質是一個 int。
emptyCtx 永遠不會取消,沒有值,也沒有截止日期。它不是 struct{},因為此型別的變數必須具有不同的地址。cancelCtx
type cancelCtx struct { Context mu sync.Mutex // 用於保護這幾個欄位的鎖,以保證 cancelCtx 是執行緒安全的 done chan struct{} // 用於獲取該 Conext 的取消通知 children map[canceler]struct{} // 用於儲存以當前節點為根節點的所有可取消的 Context,以便在根節點取消時可以把它們一併取消 err error // 用於儲存取消時指定的錯誤資訊 }
timerCtx
type timerCtx struct { cancelCtx timer *time.Timer // 由 cancelCtx.mu 來保護 deadline time.Time }
在 cancelCtx 的基礎上封裝了一個定時器和一個截止時間,這樣既可以根據需要主動取消,也可以在到達 deadline 是通過 timer 來觸發取消動作。
valueCtx
type valueCtx struct { Context key, val interface{} }
六個函式
func Background() Context
func TODO() Context
Background 和 TODO 這兩個函式內部都會建立 emptyCtx,
本質上是一樣的,主要是語義上的區別。Background 主要用於在初始化是獲取一個 Context,而 TODO 函式官方文件建議在本來應該使用外層傳遞的 ctx,而外層卻沒有傳遞的地方使用。
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
用於把一個 Context 包裝成一個 cancelCtx,並提供一個取消函式,呼叫它可以取消對應的 Context。
func WithDeadline(parent Context, d time.Time) (ctx Context, cancel CancelFunc)
func WithTimtount(parent Context, d time.Duration) (ctx Context, cancel CancelFunc)
func WithValue(parent Context, key, value interface{}) Context
用於上下文傳值。
func (c *valueCtx) Value(key interface{}) interface{} { // 用子 context 取父 context 的值時,如果 key 被判等則會出現覆蓋的情況,所以最好不要直接使用 string、int 這些基礎型別作為 key,而是每一個 Context 的 keys 都要用一種自定義型別包裝一下。 if c.key == key { return c.val } return c.Context.Value(key) }
Context 使用原則
- 不要把 Context 放在結構體中,要以引數的方式傳遞
- 以 Context 作為引數的函式方法,應該把Context作為第一個引數,放在第一位。
- 給一個函式方法傳遞 Context 的時候,不要傳遞 nil,如果不知道傳遞什麼,就使用 context.TODO
- Context 的 Value 相關方法應該傳遞必須的資料,不要什麼資料都使用這個傳遞
- Context 是執行緒安全的,可以放心的在多個 goroutine 中傳遞
本作品採用《CC 協議》,轉載必須註明作者和本文連結