欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文帶你掌握Go語言并發(fā)模式中的Context的上下文管理

 更新時間:2023年05月17日 14:47:23   作者:Go技術(shù)干貨  
在?Go?的日常開發(fā)中,Context?上下文對象無處不在,無論是處理網(wǎng)絡(luò)請求、數(shù)據(jù)庫操作還是調(diào)用?RPC?等場景,那你真的熟悉它的正確用法嗎,隨著本文一探究竟吧

前言

Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.

Go 在 1.7 引入了 context 包,目的是為了在不同的 goroutine 之間或跨 API 邊界傳遞超時、取消信號和其他請求范圍內(nèi)的值(與該請求相關(guān)的值。這些值可能包括用戶身份信息、請求處理日志、跟蹤信息等等)。

在 Go 的日常開發(fā)中,Context 上下文對象無處不在,無論是處理網(wǎng)絡(luò)請求、數(shù)據(jù)庫操作還是調(diào)用 RPC 等場景下,都會使用到 Context。那么,你真的了解它嗎?熟悉它的正確用法嗎?了解它的使用注意事項嗎?喝一杯你最喜歡的飲料,隨著本文一探究竟吧。

Context 接口

context 包在提供了一個用于跨 API 邊界傳遞超時、取消信號和其他請求范圍值的通用數(shù)據(jù)結(jié)構(gòu)。它定義了一個名為 Context 的接口,該接口包含一些方法,用于在多個 Goroutine 和函數(shù)之間傳遞請求范圍內(nèi)的信息。

以下是 Context 接口的定義:

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chanstruct{}
    Err() error
    Value(key any) any
}

Context 的核心方法

Context 的核心方法.jpg

Context 接口中有四個核心方法:Deadline()、Done()、Err()、Value()。

Deadline()

Deadline() (deadline time.Time, ok bool) 方法返回 Context 的截止時間,表示在這個時間點之后,Context 會被自動取消。如果 Context 沒有設(shè)置截止時間,該方法返回一個零值 time.Time 和一個布爾值 false

deadline, ok := ctx.Deadline()
if ok {
    // Context 有截止時間
} else {
    // Context 沒有截止時間
}

Done()

Done() 方法返回一個只讀通道,當(dāng) Context 被取消時,該通道會被關(guān)閉。你可以通過監(jiān)聽這個通道來檢測 Context 是否被取消。如果 Context 永不取消,則返回 nil

select {
case <-ctx.Done():
    // Context 已取消
default:
    // Context 尚未取消
}

Err()

Err() 方法返回一個 error 值,表示 Context 被取消時產(chǎn)生的錯誤。如果 Context 尚未取消,該方法返回 nil。

if err := ctx.Err(); err != nil {
    // Context 已取消,處理錯誤
}

Value()

Value(key any) any 方法返回與 Context 關(guān)聯(lián)的鍵值對,一般用于在 Goroutine 之間傳遞請求范圍內(nèi)的信息。如果沒有關(guān)聯(lián)的值,則返回 nil。

value := ctx.Value(key)
if value != nil {
    // 存在關(guān)聯(lián)的值
}

Context 的創(chuàng)建方式

Context 的創(chuàng)建方式.jpg

context.Background()

context.Background() 函數(shù)返回一個非 nil 的空 Context,它沒有攜帶任何的值,也沒有取消和超時信號。通常作為根 Context 使用。

ctx := context.Background()

context.TODO()

context.TODO() 函數(shù)返回一個非 nil 的空 Context,它沒有攜帶任何的值,也沒有取消和超時信號。雖然它的返回結(jié)果和 context.Background() 函數(shù)一樣,但是它們的使用場景是不一樣的,如果不確定使用哪個上下文時,可以使用 context.TODO()。

ctx := context.TODO()

context.WithValue()

context.WithValue(parent Context, key, val any) 函數(shù)接收一個父 Context 和一個鍵值對 key、val,返回一個新的子 Context,并在其中添加一個 key-value 數(shù)據(jù)對。

ctx := context.WithValue(parentCtx, "username", "陳明勇")

context.WithCancel()

context.WithCancel(parent Context) (ctx Context, cancel CancelFunc) 函數(shù)接收一個父 Context,返回一個新的子 Context 和一個取消函數(shù),當(dāng)取消函數(shù)被調(diào)用時,子 Context 會被取消,同時會向子 Context 關(guān)聯(lián)的 Done() 通道發(fā)送取消信號,屆時其衍生的子孫 Context 都會被取消。這個函數(shù)適用于手動取消操作的場景。

ctx, cancelFunc := context.WithCancel(parentCtx)  
defer cancelFunc()

context.WithCancelCause() 與 context.Cause()

context.WithCancelCause(parent Context) (ctx Context, cancel CancelCauseFunc) 函數(shù)是 Go 1.20 版本才新增的,其功能類似于 context.WithCancel(),但是它可以設(shè)置額外的取消原因,也就是 error 信息,返回的 cancel 函數(shù)被調(diào)用時,需傳入一個 error 參數(shù)。

ctx, cancelFunc := context.WithCancelCause(parentCtx)
defer cancelFunc(errors.New("原因"))

context.Cause(c Context) error 函數(shù)用于返回取消 Context 的原因,即錯誤值 error。如果是通過 context.WithCancelCause() 函數(shù)返回的取消函數(shù) cancelFunc(myErr) 進行的取消操作,我們可以獲取到 myErr 的值。否則,我們將得到與 c.Err() 相同的返回值。如果 Context 尚未被取消,將返回 nil

err := context.Cause(ctx)

context.WithDeadline()

context.WithDeadline(parent Context, d time.Time) (Context, CancelFunc) 函數(shù)接收一個父 Context 和一個截止時間作為參數(shù),返回一個新的子 Context。當(dāng)截止時間到達時,子 Context 其衍生的子孫 Context 會被自動取消。這個函數(shù)適用于需要在特定時間點取消操作的場景。

deadline := time.Now().Add(time.Second * 2)
ctx, cancelFunc := context.WithTimeout(parentCtx, deadline)
defer cancelFunc()

context.WithTimeout()

context.WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) 函數(shù)和 context.WithDeadline() 函數(shù)的功能是一樣的,其底層會調(diào)用 WithDeadline() 函數(shù),只不過其第二個參數(shù)接收的是一個超時時間,而不是截止時間。這個函數(shù)適用于需要在一段時間后取消操作的場景。

ctx, cancelFunc := context.WithTimeout(parentCtx, time.Second * 2)
defer cancelFunc()

Context 的使用場景

傳遞共享數(shù)據(jù)

編寫中間件函數(shù),用于向 HTTP 處理鏈中添加處理請求 ID 的功能。

type key int

const (
   requestIDKey key = iota
)

func WithRequestId(next http.Handler) http.Handler {
   return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
      // 從請求中提取請求ID和用戶信息
      requestID := req.Header.Get("X-Request-ID")

      // 創(chuàng)建子 context,并添加一個請求 Id 的信息
      ctx := context.WithValue(req.Context(), requestIDKey, requestID)

      // 創(chuàng)建一個新的請求,設(shè)置新 ctx
      req = req.WithContext(ctx)

      // 將帶有請求 ID 的上下文傳遞給下一個處理器
      next.ServeHTTP(rw, req)
   })
}

首先,我們從請求的頭部中提取請求 ID。然后使用 context.WithValue 創(chuàng)建一個子上下文,并將請求 ID 作為鍵值對存儲在子上下文中。接著,我們創(chuàng)建一個新的請求對象,并將子上下文設(shè)置為新請求的上下文。最后,我們將帶有請求 ID 的上下文傳遞給下一個處理器。這樣,通過使用 WithRequestId 中間件函數(shù),我們可以在處理請求的過程中方便地獲取和使用請求 ID,例如在 日志記錄、跟蹤和調(diào)試等方面。

傳遞取消信號,結(jié)束任務(wù)

啟動一個工作協(xié)程,接收到取消信號就停止工作。

package main
import (
   "context"
   "fmt"
   "time"
)
func main() {
   ctx, cancelFunc := context.WithCancel(context.Background())
   go Working(ctx)
   time.Sleep(3 * time.Second)
   cancelFunc()
   // 等待一段時間,以確保工作協(xié)程接收到取消信號并退出
   time.Sleep(1 * time.Second)
}
func Working(ctx context.Context) {
   for {
      select {
      case <-ctx.Done():
         fmt.Println("下班啦...")
         return
      default:
         fmt.Println("陳明勇正在工作中...")
      }
   }
}

執(zhí)行結(jié)果

······
······
陳明勇正在工作中...
陳明勇正在工作中...
陳明勇正在工作中...
陳明勇正在工作中...
陳明勇正在工作中...
下班啦...

在上面的示例中,我們創(chuàng)建了一個 Working 函數(shù),它會不斷執(zhí)行工作任務(wù)。我們使用 context.WithCancel 創(chuàng)建了一個上下文 ctx 和一個取消函數(shù) cancelFunc。然后,啟動了一個工作協(xié)程,并將上下文傳遞給它。

在主函數(shù)中,需要等待一段時間(3 秒)模擬業(yè)務(wù)邏輯的執(zhí)行。然后,調(diào)用取消函數(shù) cancelFunc,通知工作協(xié)程停止工作。工作協(xié)程在每次循環(huán)中都會檢查上下文的狀態(tài),一旦接收到取消信號,就會退出循環(huán)。

最后,等待一段時間(1 秒),以確保工作協(xié)程接收到取消信號并退出。

超時控制

模擬耗時操作,超時控制。

package main

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

func main() {
   // 使用 WithTimeout 創(chuàng)建一個帶有超時的上下文對象
   ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
   defer cancel()

   // 在另一個 goroutine 中執(zhí)行耗時操作
   gofunc() {
      // 模擬一個耗時的操作,例如數(shù)據(jù)庫查詢
      time.Sleep(5 * time.Second)
      cancel()
   }()

   select {
   case <-ctx.Done():
      fmt.Println("操作已超時")
   case <-time.After(10 * time.Second):
      fmt.Println("操作完成")
   }
}

執(zhí)行結(jié)果

操作已超時

在上面的例子中,首先使用 context.WithTimeout() 創(chuàng)建了一個帶有 3 秒超時的上下文對象 ctx, cancel := context.WithTimeout(ctx, 3*time.Second)。

接下來,在一個新的 goroutine 中執(zhí)行一個模擬的耗時操作,例如等待 5 秒鐘。當(dāng)耗時操作完成后,調(diào)用 cancel() 方法來取消超時上下文。

最后,在主 goroutine 中使用 select 語句等待超時上下文的完成信號。如果在 3 秒內(nèi)耗時操作完成,那么會輸出 "操作完成"。如果超過了 3 秒仍未完成,超時上下文的 Done() 通道會被關(guān)閉,輸出 "操作已超時"。

使用 Context 的一些規(guī)則

使用 Context 上下文,應(yīng)該遵循以下規(guī)則,以保持包之間的接口一致,并使靜態(tài)分析工具能夠檢查上下文傳播:

不要在結(jié)構(gòu)類型中加入 Context 參數(shù),而是將它顯式地傳遞給需要它的每個函數(shù),并且它應(yīng)該是第一個參數(shù),通常命名為 ctx:

func DoSomething(ctx context.Context, arg Arg) error {
        // ... use ctx ...
}

即使函數(shù)允許,也不要傳遞 nil Context。如果不確定要使用哪個 Context,建議使用 context.TODO()。

僅將 Context 的值用于傳輸進程和 api 的請求作用域數(shù)據(jù),不能用于向函數(shù)傳遞可選參數(shù)。

小結(jié)

本文詳細(xì)介紹了 Go 語言中的 Context 上下文,通過閱讀本文,相信你們對 Context 的功能和使用場景有所了解。同時,你們也應(yīng)該能夠根據(jù)實際需求選擇最合適的 Context 創(chuàng)建方式,并且根據(jù)規(guī)則,正確、高效地使用它。

以上就是一文帶你掌握Go語言并發(fā)模式中的Context的上下文管理的詳細(xì)內(nèi)容,更多關(guān)于Go語言Context的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go語言的隊列和堆棧實現(xiàn)方法

    Go語言的隊列和堆棧實現(xiàn)方法

    這篇文章主要介紹了Go語言的隊列和堆棧實現(xiàn)方法,涉及container/list包的使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-02-02
  • 在Golang中使用C語言代碼實例

    在Golang中使用C語言代碼實例

    這篇文章主要介紹了在Golang中使用C語言代碼實例,本文先是給出了一個Hello World例子、Golang 引用 C例子,并總結(jié)了一些要注意的地方,需要的朋友可以參考下
    2014-10-10
  • Go語言中l(wèi)og日志庫的介紹

    Go語言中l(wèi)og日志庫的介紹

    本文給大家介紹Go語言中l(wèi)og日志庫的概念使用技巧,log包定義了Logger類型,該類型提供了一些格式化輸出的方法,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2021-10-10
  • 使用Go語言提高圖片分辨率的方法與實踐

    使用Go語言提高圖片分辨率的方法與實踐

    在圖像處理和計算機視覺領(lǐng)域,提高圖片分辨率是一個常見的問題,隨著高分辨率顯示設(shè)備的普及,如4K、8K電視以及高像素手機攝像頭的應(yīng)用,用戶對高質(zhì)量圖片的需求也越來越高,本文將介紹使用Golang語言提高圖片分辨率的方法與實踐,需要的朋友可以參考下
    2023-12-12
  • Go Excelize API源碼解讀GetSheetViewOptions與SetPageLayout

    Go Excelize API源碼解讀GetSheetViewOptions與SetPageLayo

    這篇文章主要為大家介紹了Go Excelize API源碼解讀GetSheetViewOptions與SetPageLayout方法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • golang?pprof?監(jiān)控系列?go?trace統(tǒng)計原理與使用解析

    golang?pprof?監(jiān)控系列?go?trace統(tǒng)計原理與使用解析

    這篇文章主要為大家介紹了golang?pprof?監(jiān)控系列?go?trace統(tǒng)計原理與使用解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-04-04
  • 詳解Golang實現(xiàn)http重定向https的方式

    詳解Golang實現(xiàn)http重定向https的方式

    這篇文章主要介紹了詳解Golang實現(xiàn)http重定向https的方式,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-08-08
  • Go語言學(xué)習(xí)筆記之golang操作MongoDB數(shù)據(jù)庫

    Go語言學(xué)習(xí)筆記之golang操作MongoDB數(shù)據(jù)庫

    MongoDB是Nosql中常用的一種數(shù)據(jù)庫,這篇文章主要給大家介紹了關(guān)于Go語言學(xué)習(xí)筆記之golang操作MongoDB數(shù)據(jù)庫的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-05-05
  • Go語言庫系列之flag的具體使用

    Go語言庫系列之flag的具體使用

    這篇文章主要介紹了Go語言庫系列之flag的具體使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • golang 如何獲取文件夾下面的文件列表

    golang 如何獲取文件夾下面的文件列表

    這篇文章主要介紹了golang 獲取文件夾下面的文件列表方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05

最新評論