go 上下文:context.Context

落雷發表於2023-11-17

Go語言中的上下文(Context)是一種用於在 Goroutines 之間傳遞取消訊號、截止時間和其他請求範圍值的標準方式。context 包提供了 Context 型別和一些相關的函式,用於在併發程式中有效地傳遞上下文資訊。

在Go語言中,上下文通常用於以下場景:

  1. 請求的傳遞:當一個請求從客戶端傳送到伺服器時,可以使用上下文來攜帶與該請求相關的資料。這些資料可以是使用者的身份資訊、請求的後設資料或其他與請求相關的資訊。透過將上下文傳遞給處理該請求的goroutine,可以確保在整個處理過程中訪問這些資料。
  2. 取消操作:上下文可以用於取消正在進行的操作。當使用者或其他程式碼傳送取消訊號時,可以將該訊號傳遞給正在執行操作的goroutine。goroutine在接收到取消訊號後,可以根據需要執行清理操作並退出。
  3. 截止時間:有時候需要在一段時間後終止正在進行的操作。透過將截止時間與上下文一起傳遞給goroutine,可以確保在超過截止時間後執行適當的清理操作並退出。
  4. 跨多個服務通訊:當在分散式系統中使用Go語言時,上下文可以用於跨不同的服務之間傳遞請求資料、取消訊號和截止時間。透過使用上下文,可以確保在整個系統中的各個服務之間保持一致的上下文和請求生命週期管理。

透過使用上下文,可以有效地在 Goroutines 之間傳遞取消訊號、截止時間和請求範圍的值,從而更好地控制併發程式的行為。

1. context.Context 介面

Context 介面定義了在 Goroutines 之間傳遞的上下文的基本方法:

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key interface{}) interface{}
}
  • Deadline():返回上下文的截止時間。如果存在截止時間,oktrue,否則為 false
  • Done():返回一個通道,該通道關閉時表示上下文被取消或者超過了截止時間。
  • Err():返回上下文取消的原因。如果上下文沒有被取消,則返回 nil
  • Value(key):返回與給定 key 關聯的值。這允許在上下文中傳遞請求範圍的資料。

2. 建立上下文

在 Go 中,上下文可以透過 context.Background() 建立,它是一個無值的上下文,通常用作根上下文。根上下文不能被取消,也不能傳遞截止時間。

ctx := context.Background()

可以使用 context.WithCancelcontext.WithTimeoutcontext.WithDeadlinecontext.WithValue 等函式建立派生上下文,這些函式分別用於建立帶有取消、超時、截止時間和值的上下文。

// 建立一個帶有取消功能的上下文
ctx, cancel := context.WithCancel(context.Background())

// 建立一個帶有超時的上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Second)

// 建立一個帶有截止時間的上下文
deadline := time.Now().Add(2 * time.Second)
ctx, cancel := context.WithDeadline(context.Background(), deadline)

// 建立一個帶有值的上下文
key := "key"
value := "value"
ctx := context.WithValue(context.Background(), key, value)

3. 傳遞上下文

在 Go 中,透過函式引數將上下文傳遞給呼叫的函式,從而使呼叫的函式能夠感知上下文的取消或超時。例如:

func myFunction(ctx context.Context) {
    // 在這裡使用 ctx 處理邏輯
    select {
    case <-ctx.Done():
        // 上下文被取消,執行清理工作
        fmt.Println("Context canceled")
        return
    default:
        // 繼續正常的邏輯
        fmt.Println("Doing some work")
    }
}

func main() {
    // 建立帶有取消功能的上下文
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()

    // 啟動 Goroutine,傳遞上下文
    go myFunction(ctx)

    // 主 Goroutine 執行一些工作
    time.Sleep(2 * time.Second)
}

4. 上下文的取消

呼叫 cancel() 函式會取消與上下文相關的 Goroutines。一旦上下文被取消,與之關聯的所有 Goroutines 都會收到取消訊號。

ctx, cancel := context.WithCancel(context.Background())

// 啟動 Goroutine,傳遞上下文
go func(ctx context.Context) {
    select {
    case <-ctx.Done():
        // 上下文被取消,執行清理工作
        fmt.Println("Context canceled")
        return
    }
}(ctx)

// 取消上下文
cancel()

5. 上下文的超時和截止時間

使用 context.WithTimeoutcontext.WithDeadline 函式可以設定上下文的超時或截止時間。當超過指定的時間後,上下文會自動取消。

ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()

// 啟動 Goroutine,傳遞上下文
go func(ctx context.Context) {
    select {
    case <-ctx.Done():
        // 上下文超時,執行清理工作
        fmt.Println("Context timeout")
        return
    }
}(ctx)

// 主 Goroutine 執行一些工作
time.Sleep(3 * time.Second)

6. 上下文值

context.WithValue 函式可以用於在上下文中傳遞請求範圍的值。這些值可以透過 context.Value 方法在上下文中檢索。

ctx := context.WithValue(context.Background(), "user", "john_doe")

// 從上下文中獲取值
value := ctx.Value("user")
fmt.Println(value) // 輸出: john_doe

7. 上下文的鏈式呼叫

可以透過鏈式呼叫的方式,將多個上下文進行組合,形成一個父子關係的上下文鏈。

parentCtx := context.Background()
ctx1, cancel1 := context.WithTimeout(parentCtx, 2*time.Second)
defer cancel1()

ctx2, cancel2 := context.WithCancel(ctx1)
defer cancel2()

上述的 ctx2ctx1 的子上下文,當 ctx1 超時或被取消時,ctx2 也會相應地被取消。


孟斯特

宣告:本作品採用署名-非商業性使用-相同方式共享 4.0 國際 (CC BY-NC-SA 4.0)進行許可,使用時請註明出處。
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 戀水無意


相關文章