偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Go語(yǔ)言Context應(yīng)用全攻略:異步編程利器

開(kāi)發(fā) 前端
在 Go 語(yǔ)言中,Context(上下文)是一個(gè)非常重要的概念,特別是在處理請(qǐng)求時(shí)。允許在請(qǐng)求的整個(gè)生命周期內(nèi)傳遞數(shù)據(jù)、控制請(qǐng)求的取消、處理超時(shí)等。本文將介紹 Go 語(yǔ)言中 Context 的使用,幫助更好地理解與處理請(qǐng)求的傳遞與控制。

概述

在 Go 語(yǔ)言中,Context(上下文)是一個(gè)非常重要的概念,特別是在處理請(qǐng)求時(shí)。

允許在請(qǐng)求的整個(gè)生命周期內(nèi)傳遞數(shù)據(jù)、控制請(qǐng)求的取消、處理超時(shí)等。

本文將介紹 Go 語(yǔ)言中 Context 的使用,幫助更好地理解與處理請(qǐng)求的傳遞與控制。

主要內(nèi)容包括

Context 基礎(chǔ)

Context 創(chuàng)建與傳遞

Context 的超時(shí)與取消

Context 的鏈?zhǔn)讲僮?/p>

Context 在并發(fā)中的應(yīng)用

Context 的應(yīng)用場(chǎng)景

最佳實(shí)踐與注意事項(xiàng)

1. Context 基礎(chǔ)

在 Go 語(yǔ)言中,context.Context 接口定義了一個(gè)請(qǐng)求的上下文。

它包含了請(qǐng)求的截止時(shí)間、取消信號(hào)和請(qǐng)求的數(shù)據(jù)。

使用 Context 可以在請(qǐng)求之間有效地傳遞數(shù)據(jù),同時(shí)也可以控制請(qǐng)求的生命周期。

type Context interface {


Deadline() (deadline time.Time, ok bool)


Done() <-chan struct{}


Err() error


Value(key interface{}) interface{}
}

Context 接口包含了四個(gè)方法:Deadline() 返回 Context 的截止時(shí)間

Done() 返回一個(gè)通道,它會(huì)在 Context 被取消或超時(shí)時(shí)關(guān)閉

Err() 返回 Context 的錯(cuò)誤信息,Value(key) 返回 Context 中與 key 關(guān)聯(lián)的值。

2. Context 創(chuàng)建與傳遞

2.1 創(chuàng)建和傳遞 Context

package main


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


func main() {
  // 創(chuàng)建一個(gè)根Context
  rootContext := context.Background()


  // 創(chuàng)建一個(gè)帶有超時(shí)時(shí)間的Context,這里設(shè)置超時(shí)時(shí)間為2秒
  ctx, cancel := context.WithTimeout(rootContext, 2*time.Second)
  defer cancel()


  // 在新的goroutine中執(zhí)行任務(wù)
  go func(ctx context.Context) {
    select {
    case <-time.After(3 * time.Second):
      fmt.Println("任務(wù)完成")
    case <-ctx.Done():
      fmt.Println("任務(wù)取消或超時(shí)")
    }
  }(ctx)
  
  // 等待一段時(shí)間,模擬程序運(yùn)行
  time.Sleep(5 * time.Second)
}

在這個(gè)例子中,創(chuàng)建了一個(gè)帶有 2 秒超時(shí)時(shí)間的 Context,并在一個(gè)新的 goroutine 中執(zhí)行一個(gè)任務(wù)。

在主 goroutine 中,等待了 5 秒,因此任務(wù)在超時(shí)之前完成,所以會(huì)輸出"任務(wù)完成"。

2.2 使用 WithValue 傳遞數(shù)據(jù)

package main


import (
  "context"
  "fmt"
)


type key string


func main() {
  // 創(chuàng)建一個(gè)根Context
  rootContext := context.Background()


  // 使用WithValue傳遞數(shù)據(jù)
  ctx := context.WithValue(rootContext, key("userID"), 123)


  // 在子函數(shù)中獲取傳遞的數(shù)據(jù)
  getUserID(ctx)
}


func getUserID(ctx context.Context) {
  // 從Context中獲取數(shù)據(jù)
  if userID, ok := ctx.Value(key("userID")).(int); ok {
    fmt.Println("UserID:", userID)
  } else {
    fmt.Println("UserID不存在")
  }
}

在這個(gè)示例中,使用 WithValue 方法在 Context 中傳遞了一個(gè) userID 的值,并在 getUserID 函數(shù)中成功獲取并打印了這個(gè)值。

3. Context 的超時(shí)與取消

3.1 設(shè)置請(qǐng)求超時(shí)時(shí)間

package main


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


func main() {
  // 創(chuàng)建一個(gè)根Context
  rootContext := context.Background()


  // 創(chuàng)建一個(gè)超時(shí)時(shí)間為2秒的Context
  timeoutCtx, _ := context.WithTimeout(rootContext, 2*time.Second)


  // 創(chuàng)建一個(gè)手動(dòng)取消的Context
  cancelCtx, cancel := context.WithCancel(rootContext)
  defer cancel()


  // 在新的goroutine中執(zhí)行任務(wù)
  go func(ctx context.Context) {
    select {
    case <-time.After(3 * time.Second):
      fmt.Println("任務(wù)完成")
    case <-ctx.Done():
      fmt.Println("任務(wù)取消或超時(shí)")
    }
  }(timeoutCtx)


  // 在另一個(gè)goroutine中執(zhí)行任務(wù)
  go func(ctx context.Context) {
    select {
    case <-time.After(1 * time.Second):
      fmt.Println("另一個(gè)任務(wù)完成")
    case <-ctx.Done():
      fmt.Println("另一個(gè)任務(wù)取消")
    }
  }(cancelCtx)


  // 等待一段時(shí)間,模擬程序運(yùn)行
  time.Sleep(5 * time.Second)
}

在上面例子中,用 WithTimeout 方法創(chuàng)建了一個(gè)帶有 2 秒超時(shí)時(shí)間的 Context。

在任務(wù)的 goroutine 中,用 select 語(yǔ)句監(jiān)聽(tīng)了超時(shí)和 Context 的取消兩個(gè)事件,以便及時(shí)響應(yīng)。

3.2 處理請(qǐng)求取消

package main


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


func main() {
  // 創(chuàng)建一個(gè)根Context
  rootContext := context.Background()


  // 創(chuàng)建一個(gè)可以手動(dòng)取消的Context
  ctx, cancel := context.WithCancel(rootContext)
  defer cancel()


  // 在新的goroutine中執(zhí)行任務(wù)
  go func(ctx context.Context) {
    select {
    case <-time.After(3 * time.Second):
      fmt.Println("任務(wù)完成")
    case <-ctx.Done():
      fmt.Println("任務(wù)取消")
    }
  }(ctx)


  // 等待一段時(shí)間,手動(dòng)取消任務(wù)
  time.Sleep(2 * time.Second)
  cancel()


  // 等待一段時(shí)間,模擬程序運(yùn)行
  time.Sleep(1 * time.Second)
}

在上面例子中,使用 WithCancel 方法創(chuàng)建了一個(gè)可以手動(dòng)取消的 Context。

在主函數(shù)中,等待了 2 秒后,手動(dòng)調(diào)用 cancel 函數(shù)取消了任務(wù)。

這時(shí),在任務(wù)的 goroutine 中,ctx.Done() 會(huì)接收到取消信號(hào),從而退出任務(wù)。

4. Context 的鏈?zhǔn)讲僮?/h2>

在實(shí)際應(yīng)用中,可能需要將多個(gè) Context 串聯(lián)起來(lái)使用。

Go 語(yǔ)言的 Context 提供了 WithCancel、WithDeadline、WithTimeout 等方法。

可以用這些方法實(shí)現(xiàn)多個(gè) Context 的協(xié)同工作。

package main


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


func main() {
  // 創(chuàng)建一個(gè)根Context
  rootContext := context.Background()


  // 創(chuàng)建一個(gè)超時(shí)時(shí)間為2秒的Context
  timeoutCtx, _ := context.WithTimeout(rootContext, 2*time.Second)


  // 創(chuàng)建一個(gè)手動(dòng)取消的Context
  cancelCtx, cancel := context.WithCancel(rootContext)
  defer cancel()


  // 在新的goroutine中執(zhí)行任務(wù)
  go func(ctx context.Context) {
    select {
    case <-time.After(3 * time.Second):
      fmt.Println("任務(wù)完成")
    case <-ctx.Done():
      fmt.Println("任務(wù)取消或超時(shí)")
    }
  }(timeoutCtx)


  // 在另一個(gè)goroutine中執(zhí)行任務(wù)
  go func(ctx context.Context) {
    select {
    case <-time.After(1 * time.Second):
      fmt.Println("另一個(gè)任務(wù)完成")
    case <-ctx.Done():
      fmt.Println("另一個(gè)任務(wù)取消")
    }
  }(cancelCtx)


  // 等待一段時(shí)間,模擬程序運(yùn)行
  time.Sleep(5 * time.Second)
}

在示例中,創(chuàng)建了一個(gè)帶有 2 秒超時(shí)時(shí)間的 Context 和一個(gè)可以手動(dòng)取消的 Context,然后分別傳遞給兩個(gè)不同的任務(wù)。

在主函數(shù)中,等待了 5 秒,超時(shí)時(shí)間為 2 秒,因此第一個(gè)任務(wù)會(huì)因超時(shí)而取消,第二個(gè)任務(wù)則會(huì)在 1 秒后完成。

5. Context 在并發(fā)中的應(yīng)用

5.1 使用 Context 控制多個(gè)協(xié)程

package main


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


func main() {
  // 創(chuàng)建一個(gè)根Context
  rootContext := context.Background()


  // 創(chuàng)建一個(gè)可以手動(dòng)取消的Context
  ctx, cancel := context.WithCancel(rootContext)
  defer cancel()


  // 使用WaitGroup等待所有任務(wù)完成
  var wg sync.WaitGroup


  // 啟動(dòng)多個(gè)協(xié)程執(zhí)行任務(wù)
  for i := 0; i < 5; i++ {
    wg.Add(1)
    go func(id int) {
      defer wg.Done()
      select {
      case <-time.After(time.Duration(id) * time.Second):
        fmt.Println("任務(wù)", id, "完成")
      case <-ctx.Done():
        fmt.Println("任務(wù)", id, "取消")
      }
    }(i)
  }


  // 等待一段時(shí)間,然后手動(dòng)取消任務(wù)
  time.Sleep(2 * time.Second)
  cancel()


  // 等待所有任務(wù)完成
  wg.Wait()
}

在上面例子中,創(chuàng)建了一個(gè)可以手動(dòng)取消的 Context,并使用 sync.WaitGroup 等待所有任務(wù)完成。

在 for 循環(huán)中,啟動(dòng)了 5 個(gè)協(xié)程,每個(gè)協(xié)程會(huì)等待一段時(shí)間后輸出任務(wù)完成信息。

在主函數(shù)中,程序等待了 2 秒后,手動(dòng)調(diào)用 cancel 函數(shù)取消了任務(wù),協(xié)程會(huì)接收到取消信號(hào)并退出。

5.2 避免 Context 濫用

在使用 Context 時(shí),要避免將 Context 放在結(jié)構(gòu)體中。

因?yàn)?Context 應(yīng)該作為函數(shù)參數(shù)傳遞,而不應(yīng)該被放在結(jié)構(gòu)體中進(jìn)行傳遞。

Context 應(yīng)該限定在程序的最小作用域,不要傳遞到不需要它的函數(shù)中。

6. Context 的應(yīng)用場(chǎng)景

6.1 HTTP 請(qǐng)求中的 Context 使用

package main


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


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


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


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

在上面示例中,創(chuàng)建了一個(gè) HTTP 請(qǐng)求處理函數(shù) handler。

在處理函數(shù)中,用 r.Context() 獲取到請(qǐng)求的 Context,并在其中執(zhí)行一個(gè)耗時(shí)的任務(wù)。

如果請(qǐng)求超時(shí),ctx.Done() 會(huì)接收到取消信號(hào),可以在其中處理請(qǐng)求超時(shí)的邏輯。

6.2 數(shù)據(jù)庫(kù)操作中的 Context 使用

package main


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


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


func main() {
  // 連接數(shù)據(jù)庫(kù)
  db, err := sql.Open("mysql", "username:password@tcp(localhost:3306)/database")
  if err != nil {
    fmt.Println("數(shù)據(jù)庫(kù)連接失敗:", err)
    return
  }
  defer db.Close()


  // 創(chuàng)建一個(gè)Context,設(shè)置超時(shí)時(shí)間為5秒
  ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  defer cancel()


  // 在Context的超時(shí)時(shí)間內(nèi)執(zhí)行數(shù)據(jù)庫(kù)查詢
  rows, err := db.QueryContext(ctx, "SELECT * FROM users")
  if err != nil {
    fmt.Println("數(shù)據(jù)庫(kù)查詢失敗:", err)
    return
  }
  defer rows.Close()


  // 處理查詢結(jié)果
  for rows.Next() {
    // 處理每一行數(shù)據(jù)
  }
}

在上面例子中,使用 database/sql 包進(jìn)行數(shù)據(jù)庫(kù)查詢。創(chuàng)建了一個(gè)帶有 5 秒超時(shí)時(shí)間的 Context,并在其中執(zhí)行數(shù)據(jù)庫(kù)查詢。

如果查詢時(shí)間超過(guò) 5 秒,Context 會(huì)接收到取消信號(hào),可以在其中執(zhí)行處理查詢超時(shí)的邏輯。

6.3 其他業(yè)務(wù)場(chǎng)景中的 Context 使用

在其他業(yè)務(wù)場(chǎng)景中,可使用 Context 實(shí)現(xiàn)更多復(fù)雜的任務(wù)協(xié)同。

例如,使用 Context 在多個(gè)微服務(wù)之間進(jìn)行數(shù)據(jù)傳遞和超時(shí)控制。

以下是一個(gè)示例,演示了如何在微服務(wù)架構(gòu)中使用 Context 進(jìn)行跨服務(wù)的數(shù)據(jù)傳遞

package main


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


type Request struct {
  ID int
}


type Response struct {
  Message string
}


func microservice(ctx context.Context, reqCh chan Request, resCh chan Response) {
  for {
    select {
    case <-ctx.Done():
      fmt.Println("Microservice shutting down...")
      return
    case req := <-reqCh:
      // 模擬處理請(qǐng)求的耗時(shí)操作
      time.Sleep(2 * time.Second)
      response := Response{Message: fmt.Sprintf("Processed request with ID %d", req.ID)}
      resCh <- response
    }
  }
}


func main() {
  // 創(chuàng)建根Context
  rootContext := context.Background()


  // 創(chuàng)建用于請(qǐng)求和響應(yīng)的通道
  reqCh := make(chan Request)
  resCh := make(chan Response)


  // 啟動(dòng)微服務(wù)
  go microservice(rootContext, reqCh, resCh)


  // 創(chuàng)建帶有5秒超時(shí)時(shí)間的Context
  ctx, cancel := context.WithTimeout(rootContext, 5*time.Second)
  defer cancel()


  // 發(fā)送請(qǐng)求到微服務(wù)
  for i := 1; i <= 3; i++ {
    req := Request{ID: i}
    reqCh <- req


    select {
    case <-ctx.Done():
      fmt.Println("Request timed out!")
      return
    case res := <-resCh:
      fmt.Println(res.Message)
    }
  }
}

在上面示例中,創(chuàng)建了一個(gè)簡(jiǎn)單的微服務(wù)模擬,它接收來(lái)自 reqCh 通道的請(qǐng)求,并將處理結(jié)果發(fā)送到 resCh 通道。

在主函數(shù)中,用帶有 5 秒超時(shí)時(shí)間的 Context 來(lái)確保請(qǐng)求不會(huì)無(wú)限期等待,同時(shí)也能夠處理超時(shí)的情況。

7. 最佳實(shí)踐與注意事項(xiàng)

7.1 避免在函數(shù)庫(kù)中使用 Context

通常情況下,應(yīng)該在函數(shù)的參數(shù)列表中顯式傳遞 Context,而不是將 Context 放在結(jié)構(gòu)體中。

這樣做可以使函數(shù)的行為更加明確,避免隱藏傳遞的 Context,提高代碼的可讀性和可維護(hù)性。

7.2 避免在結(jié)構(gòu)體中嵌入 Context

盡管可以將 Context 作為結(jié)構(gòu)體的成員嵌入,但這樣的做法通常是不推薦的。

因?yàn)?Context 應(yīng)該是在函數(shù)調(diào)用的時(shí)候傳遞,而不是嵌入在結(jié)構(gòu)體中。

如果結(jié)構(gòu)體的方法需要使用 Context,應(yīng)該將 Context 作為參數(shù)傳遞給這些方法。

7.3 注意 Context 的傳遞路徑

在實(shí)際應(yīng)用中,要仔細(xì)考慮 Context 的傳遞路徑。

若是在多個(gè)函數(shù)之間傳遞 Context,確保 Context 的傳遞路徑清晰明了,避免出現(xiàn)歧義和混亂。

Context 的傳遞路徑應(yīng)該盡量短,不要跨越過(guò)多的函數(shù)調(diào)用。

總結(jié)

在 Go 語(yǔ)言中,Context 是一個(gè)強(qiáng)大的工具,用于處理請(qǐng)求的傳遞、控制和超時(shí)等。

通過(guò)合理地使用 Context,可以編寫出更加穩(wěn)定、高效的異步程序,提高系統(tǒng)的健壯性和可維護(hù)性。

責(zé)任編輯:武曉燕 來(lái)源: Go先鋒
相關(guān)推薦

2023-10-13 19:42:00

2017-07-06 17:39:53

JavaScript開(kāi)發(fā)程序員

2009-02-12 10:12:00

NAT配置

2009-02-10 09:47:00

應(yīng)用程序訪問(wèn)權(quán)限

2010-11-18 23:24:42

云遷移

2010-04-23 14:04:23

Oracle日期操作

2013-04-15 10:48:16

Xcode ARC詳解iOS ARC使用

2024-05-07 09:01:21

Queue 模塊Python線程安全隊(duì)列

2013-06-08 11:13:00

Android開(kāi)發(fā)XML解析

2009-02-20 11:43:22

UNIXfish全攻略

2014-03-19 17:22:33

2009-10-19 15:20:01

家庭綜合布線

2009-12-14 14:32:38

動(dòng)態(tài)路由配置

2013-09-09 15:05:12

iOS應(yīng)用內(nèi)置付費(fèi)IAP完全攻略

2009-10-12 15:06:59

2015-03-04 13:53:33

MySQL數(shù)據(jù)庫(kù)優(yōu)化SQL優(yōu)化

2009-07-04 11:26:12

unix應(yīng)急安全攻略

2022-10-21 11:30:42

用戶生命周期分析

2010-10-11 13:54:03

Windows Ser

2020-12-28 10:50:09

Linux環(huán)境變量命令
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)