Go context 介紹

Undefined443發表於2024-05-23

在 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

  1. context.Background()
  2. context.TODO()
  3. context.WithCancel(parent Context)
  4. context.WithTimeout(parent Context, timeout time.Duration)
  5. context.WithDeadline(parent Context, deadline time.Time)
  6. 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)
}

典型應用場景

  1. API 請求處理: 在處理 HTTP 請求時,傳遞上下文以管理超時和取消。
  2. 併發任務管理: 使用上下文來控制和取消多個併發任務。
  3. 傳遞請求範圍的資料: 例如使用者身份驗證資訊、跟蹤 ID 等。

注意事項

  • Context 是不可變的,應當透過 context.WithCancel, context.WithTimeout, context.WithDeadline, context.WithValue 等函式建立新的派生上下文。
  • 不要將 Context 儲存在結構體中,應當作為函式的第一個引數傳遞。
  • 儘量在短生命週期的請求中使用 Context,避免長時間持有上下文。

透過正確使用 context 包,可以編寫出更健壯、更易於維護的併發程式。

相關文章