成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Go語言高級特性:Context深入解讀

開發 前端
在 Go 語言中,context(上下文)是一個非常重要的概念。它主要用于在多個 goroutine 之間傳遞請求特定任務的截止日期、取消信號以及其他請求范圍的值。3. Context 的取消與超時,本文將探討 Go 語言中context的用法,從基礎概念到實際應用,將全面了解上下文的使用方法。

概述

在 Go 語言中,context(上下文)是一個非常重要的概念。

它主要用于在多個 goroutine 之間傳遞請求特定任務的截止日期、取消信號以及其他請求范圍的值。3. Context 的取消與超時

本文將探討 Go 語言中context的用法,從基礎概念到實際應用,將全面了解上下文的使用方法。

主要內容包括

什么是 Context(上下文)

Context 的基本用法:創建與傳遞

Context 的取消與超時

Context 的值傳遞

實際應用場景:HTTP 請求的 Context 使用

數據庫操作中的 Context 應用

自定義 Context 的實現

Context 的生命周期管理

Context 的注意事項

1. 什么是 Context(上下文)

在 Go 語言中,context(上下文)是一個接口,定義了四個方法:Deadline()、Done()、Err()和Value(key interface{})。

它主要用于在 goroutine 之間傳遞請求的截止日期、取消信號以及其他請求范圍的值。

2. Context 的基本用法:創建與傳遞

2.1 創建 Context

package main


import (
  "context"
  "fmt"
)


func main() {
  // 創建一個空的Context
  ctx := context.Background()


  // 創建一個帶有取消信號的Context
  _, cancel := context.WithCancel(ctx)
  defer cancel() // 延遲調用cancel,確保在函數退出時取消Context


  fmt.Println("Context創建成功")
}

以上代碼演示了如何創建一個空的context和一個帶有取消信號的context。

使用context.WithCancel(parent)可以創建一個帶有取消信號的子context。

在調用cancel函數時,所有基于該context的操作都會收到取消信號。

2.2 傳遞 Context

package main


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


func worker(ctx context.Context) {
  for {
    select {
    case <-ctx.Done():
      fmt.Println("收到取消信號,任務結束")
      return


    default:
      fmt.Println("正在執行任務...")
      time.Sleep(1 * time.Second)
    }


  }
}


func main() {
  ctx := context.Background()


  ctxWithCancel, cancel := context.WithCancel(ctx)


  defer cancel()


  go worker(ctxWithCancel)


  time.Sleep(3 * time.Second)
  cancel() // 發送取消信號
  time.Sleep(1 * time.Second)
}

在上面例子中,創建了一個帶有取消信號的context,并將它傳遞給一個 goroutine 中的任務。

調用cancel函數,可以發送取消信號,中斷任務的執行。

3.Context 的取消與超時

3.1 使用 Context 實現取消

package main


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


func main() {
  ctx := context.Background()
  ctxWithCancel, cancel := context.WithCancel(ctx)


  go func() {
    select {
    case <-ctxWithCancel.Done():
      fmt.Println("收到取消信號,任務結束")
    case <-time.After(2 * time.Second):
      fmt.Println("任務完成")
    }
  }()


  time.Sleep(1 * time.Second)
  
  cancel() // 發送取消信號
  
  time.Sleep(1 * time.Second)
}

在這個例子中,使用time.After函數模擬一個長時間運行的任務。

如果任務在 2 秒內完成,就打印“任務完成”,否則在接收到取消信號后打印“收到取消信號,任務結束”。

3.2 使用 Context 實現超時控制

package main


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


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


  select {
  case <-ctxWithTimeout.Done():
    fmt.Println("超時,任務結束")
  case <-time.After(1 * time.Second):
    fmt.Println("任務完成")
  }
}

在上面例子中,使用context.WithTimeout(parent, duration)函數創建了一個在 2 秒后自動取消的context

如果任務在 1 秒內完成,就打印“任務完成”,否則在 2 秒后打印“超時,任務結束”。

4. Context 的值傳遞

4.1 使用 WithValue 傳遞值

package main


import (
  "context"
  "fmt"
)


type key string


func main() {
  ctx := context.WithValue(context.Background(), key("name"), "Alice")
  value := ctx.Value(key("name"))
  fmt.Println("Name:", value)
}

在上面例子中,使用context.WithValue(parent, key, value)函數在context中傳遞了一個鍵值對。

使用ctx.Value(key)函數可以獲取傳遞的值。

5. 實際應用場景:HTTP 請求的 Context 使用

5.1 使用 Context 處理 HTTP 請求

package main


import (
  "fmt"
  "net/http"
  "time"
)


func handler(w http.ResponseWriter, r *http.Request) {
  ctx := r.Context()


  select {
  case <-time.After(3 * time.Second):
    fmt.Fprintln(w, "Hello, World!")
  case <-ctx.Done():
    err := ctx.Err()
    fmt.Println("處理請求:", err)
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
}


func main() {
  http.HandleFunc("/", handler)
  http.ListenAndServe(":8080", nil)
}

在上面例子中,使用http.Request結構體中的Context()方法獲取到請求的context,并在處理請求時設置了 3 秒的超時時間。

如果請求在 3 秒內完成,就返回“Hello, World!”,否則返回一個錯誤。

5.2 處理 HTTP 請求的超時與取消

package main


import (
  "context"
  "fmt"
  "net/http"
  "time"
)


func handler(w http.ResponseWriter, r *http.Request) {
  ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
  defer cancel()


  select {
  case <-time.After(3 * time.Second):
    fmt.Fprintln(w, "Hello, World!")
  case <-ctx.Done():
    err := ctx.Err()
    fmt.Println("處理請求:", err)
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
}


func main() {
  http.HandleFunc("/", handler)
  http.ListenAndServe(":8080", nil)
}

在上面例子中,使用context.WithTimeout(parent, duration)函數為每個請求設置了 2 秒的超時時間。

如果請求在 2 秒內完成,就返回“Hello, World!”,否則返回一個錯誤。

6. 數據庫操作中的 Context 應用

6.1 使用 Context 控制數據庫查詢的超時

package main


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


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


func main() {
  db, err := sql.Open("mysql", "username:password@tcp(localhost:3306)/database")
  
  if err != nil {
    fmt.Println(err)
    return
  }
  
  defer db.Close()


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


  rows, err := db.QueryContext(ctx, "SELECT * FROM users")
  
  if err != nil {
    fmt.Println("查詢出錯:", err)
    return
  }
  
  defer rows.Close()


  // 處理查詢結果
}

在上面例子中,使用context.WithTimeout(parent, duration)函數為數據庫查詢設置了 2 秒的超時時間,確保在 2 秒內完成查詢操作。

如果查詢超時,程序會收到context的取消信號,從而中斷數據庫查詢。

6.2 使用 Context 取消長時間運行的數據庫事務

package main


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


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


func main() {


  db, err := sql.Open("mysql", "username:password@tcp(localhost:3306)/database")
  
  if err != nil {
    fmt.Println(err)
    return
  }
  defer db.Close()


  tx, err := db.Begin()
  if err != nil {
    fmt.Println(err)
    return
  }


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


  go func() {
    <-ctx.Done()
    
    fmt.Println("接收到取消信號,回滾事務")
    
    tx.Rollback()
  }()


  // 執行長時間運行的事務操作
  // ...


  err = tx.Commit()
  if err != nil {
  
    fmt.Println("提交事務出錯:", err)
    
    return
  }


  fmt.Println("事務提交成功")
}

在這個例子中,使用context.WithTimeout(parent, duration)函數為數據庫事務設置了 2 秒的超時時間。

同時使用 goroutine 監聽context的取消信號。

在 2 秒內事務沒有完成,程序會收到context的取消信號,從而回滾事務。

7. 自定義 Context 的實現

7.1 實現自定義的 Context 類型

package main


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


type MyContext struct {
  context.Context
  RequestID string
}


func WithRequestID(ctx context.Context, requestID string) *MyContext {
  return &MyContext{
    Context:   ctx,
    RequestID: requestID,
  }
}


func (c *MyContext) Deadline() (deadline time.Time, ok bool) {
  return c.Context.Deadline()
}


func (c *MyContext) Done() <-chan struct{} {
  return c.Context.Done()
}


func (c *MyContext) Err() error {
  return c.Context.Err()
}


func (c *MyContext) Value(key interface{}) interface{} {
  if key == "requestID" {
    return c.RequestID
  }
  return c.Context.Value(key)
}


func main() {
  ctx := context.Background()
  
  ctxWithRequestID := WithRequestID(ctx, "12345")


  requestID := ctxWithRequestID.Value("requestID").(string)
  
  fmt.Println("Request ID:", requestID)
}

在這個例子中,實現了一個自定義的MyContext類型,它包含了一個RequestID字段。

通過WithRequestID函數,可以在原有的context上附加一個RequestID值,然后在需要的時候獲取這個值。

7.2 自定義 Context 的應用場景

自定義context類型的應用場景非常廣泛,比如在微服務架構中,

可在context中加入一些額外的信息,比如用戶 ID、請求來源等,以便在服務調用鏈路中傳遞這些信息。

8. Context 的生命周期管理

8.1 Context 的生命周期

context.Context 是不可變的,一旦創建就不能修改。它的值在傳遞時是安全的,可以被多個 goroutine 共享。

在一個請求處理的生命周期內,通常會創建一個頂級的 Context,然后從這個頂級的 Context 派生出子 Context,傳遞給具體的處理函數。

8.2 Context 的取消

當請求處理完成或者發生錯誤時,應該主動調用 context.WithCancel 或 context.WithTimeout 等函數創建的 Context 的 Cancel 函數來通知子 goroutines 停止工作。

這樣可以確保資源被及時釋放,避免 goroutine 泄漏。

func handleRequest(ctx context.Context) {


    // 派生一個新的 Context,設置超時時間
    ctx, cancel := context.WithTimeout(ctx, time.Second)
    
    defer cancel() // 確保在函數退出時調用 cancel,防止資源泄漏


    // 在這個新的 Context 下進行操作
    // ...
}

8.3 Context 的傳遞

在函數之間傳遞 Context 時,確保傳遞的是必要的最小信息。避免在函數間傳遞整個 Context,而是選擇傳遞 Context 的衍生物。

如 context.WithValue 創建的 Context,其中包含了特定的鍵值對信息。

func middleware(next http.Handler) http.Handler {


return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 從請求中獲取特定的信息
userID := extractUserID(r)


// 將特定的信息存儲到 Context 中
ctx := context.WithValue(r.Context(), userIDKey, userID)


// 將新的 Context 傳遞給下一個處理器
next.ServeHTTP(w, r.WithContext(ctx))


})
}

9. Context 的注意事項

9.1 不要在函數簽名中傳遞 Context

避免在函數的參數列表中傳遞 context.Context,除非確實需要用到它。

如果函數的邏輯只需要使用 Context 的部分功能,那么最好只傳遞它需要的具體信息,而不是整個 Context。

// 不推薦的做法
func processRequest(ctx context.Context) {
    // ...
}


// 推薦的做法
func processRequest(userID int, timeout time.Duration) {
    // 使用 userID 和 timeout,而不是整個 Context
}

9.2 避免在結構體中濫用 Context

不要在結構體中保存 context.Context,因為它會增加結構體的復雜性。

若是需要在結構體中使用 Context 的值,最好將需要的值作為結構體的字段傳遞進來。

type MyStruct struct {
    // 不推薦的做法
    Ctx context.Context
    
    // 推薦的做法
    UserID int
}
責任編輯:武曉燕 來源: Go先鋒
相關推薦

2023-09-21 22:02:22

Go語言高級特性

2022-10-30 23:13:30

contextGo語言

2021-07-15 23:18:48

Go語言并發

2023-11-06 13:32:38

Go編程

2018-12-19 14:40:08

Redis高級特性

2021-10-16 17:53:35

Go函數編程

2023-11-30 08:09:02

Go語言

2023-11-06 08:14:51

Go語言Context

2013-05-28 09:43:38

GoGo語言并發模式

2014-04-24 10:48:27

Go語言基礎實現

2024-06-05 14:35:26

2018-09-20 17:30:01

2009-12-15 14:16:13

Ruby Contin

2021-01-26 05:19:56

語言Go Context

2014-08-05 13:09:34

Objective-C動態特性

2024-01-03 15:09:21

云原生Go語言

2022-05-05 11:20:08

KubernetesDocker云計算

2023-10-04 00:18:00

云原生Go語言

2021-01-14 05:20:48

Go語言泛型

2024-04-26 12:56:17

Go編程語言
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久精品一级 | 免费精品| 午夜色播 | www亚洲精品 | 欧美成人精品一区二区男人看 | 免费在线毛片 | 成人无遮挡毛片免费看 | 亚洲一区二区高清 | 中文字幕视频一区二区 | 久久精品二区亚洲w码 | 精品一二区 | 日韩久久久久 | 无码国模国产在线观看 | 日韩精品一区二区三区中文字幕 | 黄频免费 | 日本三级电影在线看 | 久草资源网站 | 色婷婷av一区二区三区软件 | 国产综合区 | 91成人免费看片 | 午夜网址 | 97色在线观看免费视频 | 国产亚洲精品美女久久久久久久久久 | 日日干夜夜操天天操 | 久久久99精品免费观看 | 91在线精品秘密一区二区 | 日韩一区二区三区精品 | 在线国产一区 | 久久亚洲一区二区三区四区 | 欧美综合一区二区 | 国产剧情一区 | 国产一区二区在线视频 | a在线视频 | 精品国产一区二区三区免费 | 国产女人精品视频 | 亚洲精品一二三区 | 国产精品久久影院 | 中文字幕一区二区三区四区 | 国产片一区二区三区 | 国产精品一区在线观看 | 91欧美精品 |