在 Go 程式語言中,context
包提供了一個用於在 goroutine 之間傳遞上下文資訊的方法。它通常用於控制 goroutine 的生命週期、傳遞請求範圍內的資料、以及處理超時或取消訊號。context
包的核心是 Context
介面和與之相關的函式和方法。
Context
介面
Context
介面定義如下:
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
Deadline()
返回上下文會被自動取消的時間,以及是否存在這樣的時間。Done()
返回一個chan struct{}
,當上下文被取消或超時時,這個通道會被關閉。Err()
返回上下文被取消的原因。如果Done
通道已經關閉,它會返回一個非 nil 的錯誤。Value(key interface{})
返回與上下文關聯的鍵對應的值。
建立 Context
通常有四種方式來建立一個 Context
:
context.Background()
context.TODO()
context.WithCancel(parent Context)
context.WithTimeout(parent Context, timeout time.Duration)
context.WithDeadline(parent Context, deadline time.Time)
context.WithValue(parent Context, key, val interface{})
例子
使用 context.Background()
context.Background()
返回一個空的上下文,通常用於主函式、初始化和測試。
ctx := context.Background()
使用 context.WithCancel
context.WithCancel
返回一個派生上下文和一個取消函式。當呼叫取消函式時,派生上下文的 Done
通道會被關閉。
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // 確保資源被釋放
go func() {
// 模擬一些工作
time.Sleep(2 * time.Second)
cancel() // 取消上下文
}()
<-ctx.Done() // 等待上下文被取消
fmt.Println("Context canceled:", ctx.Err())
使用 context.WithTimeout
context.WithTimeout
返回一個派生上下文和一個取消函式。指定的超時時間過後,派生上下文的 Done
通道會被關閉。
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
select {
case <-time.After(5 * time.Second):
fmt.Println("Operation completed")
case <-ctx.Done():
fmt.Println("Timeout:", ctx.Err())
}
使用 context.WithValue
context.WithValue
返回一個帶有鍵值對的派生上下文,用於在請求的生命週期中傳遞資料。
type keyType string
func main() {
ctx := context.WithValue(context.Background(), keyType("userID"), 12345)
ProcessRequest(ctx)
}
func ProcessRequest(ctx context.Context) {
userID := ctx.Value(keyType("userID")).(int)
fmt.Println("User ID:", userID)
}
典型應用場景
- API 請求處理: 在處理 HTTP 請求時,傳遞上下文以管理超時和取消。
- 併發任務管理: 使用上下文來控制和取消多個併發任務。
- 傳遞請求範圍的資料: 例如使用者身份驗證資訊、跟蹤 ID 等。
注意事項
Context
是不可變的,應當透過context.WithCancel
,context.WithTimeout
,context.WithDeadline
,context.WithValue
等函式建立新的派生上下文。- 不要將
Context
儲存在結構體中,應當作為函式的第一個引數傳遞。 - 儘量在短生命週期的請求中使用
Context
,避免長時間持有上下文。
透過正確使用 context
包,可以編寫出更健壯、更易於維護的併發程式。