Go 編程實踐 - Go Context

1、context 包的引入

context 包是在 Go 1.7 引入的,它爲在多個 goroutine 之間傳遞請求範圍的上下文信息和控制 goroutine 的生命週期提供了一種標準的方式。

2、context 包的主要作用

**傳遞上下文信息:**context 可用於在 goroutine 之間傳遞請求範圍的數據,如請求的認證信息、請求的截止時間、請求的 ID 等。這些信息在 goroutine 之間傳遞時可以保持一致性。
**控制 goroutine 生命週期:**可以使用 context 來通知 goroutine 取消操作,避免資源浪費和不必要的計算,尤其在請求超時、請求失敗的情況下。
**管理超時和截止時間:**設置超時或截止時間,當達到超時或截止時間時,goroutine 可以提前結束,防止長時間運行的 goroutine 導致資源耗盡或性能問題。

3、基本使用方法
3.1、創建 Context
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
ctx, cancel := context.WithTimeout(ctx, time.Second*5)
deadline := time.Now().Add(time.Second * 5)
ctx, cancel := context.WithDeadline(ctx, deadline)
ctx = context.WithValue(ctx, "requestID""123456")
3.2、檢查 Context 狀態
select {
case <-ctx.Done():
    fmt.Println("Context done:", ctx.Err())
default:
    // 正常操作
}
requestID := ctx.Value("requestID").(string)
4、經典使用場景
4.1、設置取消

一個請求觸發多個 goroutine 執行任務,當其中一個任務完成或失敗時,需要取消其它 goroutine 的操作。

package main

import (
 "context"
 "fmt"
 "time"
)

func main() {
 // 創建一個可取消的 Context
 ctx, cancel := context.WithCancel(context.Background())

 go func(ctx context.Context) {
  for {
   select {
   case <-ctx.Done():
    fmt.Println("Goroutine 1: Operation canceled")
    return
   default:
    fmt.Println("Goroutine 1: Working...")
    time.Sleep(time.Second * 1)
   }
  }
 }(ctx)

 go func(ctx context.Context) {
  for {
   select {
   case <-ctx.Done():
    fmt.Println("Goroutine 2: Operation canceled")
    return
   default:
    fmt.Println("Goroutine 2: Working...")
    time.Sleep(time.Second * 2)
   }
  }
 }(ctx)

 // 模擬操作完成或失敗,調用取消函數
 time.Sleep(time.Second * 3)
 cancel()

 // 等待一段時間,觀察輸出
 time.Sleep(time.Second * 2)
}
4.2、設置超時

Go 的 database/sql 標準庫中的 DB.QueryContext、DB.ExecContext 等方法都可接受 context.Context 參數,能在數據庫操作時傳遞上下文。

package main

import (
 "context"
 "database/sql"
 "fmt"
 "log"
 "time"

 _ "github.com/go-sql-driver/mysql"
)

func main() {
 // 連接數據庫
 dsn := "user:password@tcp(localhost:3306)/dbname?parseTime=True&loc=Local&charset=utf8mb4"

 db, err := sql.Open("mysql", dsn)
 if err != nil {
  log.Fatal(err)
 }
 defer db.Close()

 // 設置 3 秒超時
 ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
 defer cancel()

 // 執行查詢
 //db.Query()
 rows, err := db.QueryContext(ctx, "select id, name from tb1 limit 3")
 //rows, err := db.QueryContext(ctx, "select sleep(5) as id, 'abc'") // context deadline exceeded
 if err != nil {
  log.Fatal(err)
 }
 defer rows.Close()

 // 處理查詢
 for rows.Next() {
  var id int
  var name string
  if err := rows.Scan(&id, &name); err != nil {
   log.Fatal(err)
  }
  fmt.Printf("ID: %d, Name: %s\n", id, name)
 }
}
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/m4Kyj0g2ulH53EjTRrrfsQ