亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

前言

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 邊界傳遞超時(shí)、取消信號(hào)和其他請(qǐng)求范圍內(nèi)的值(與該請(qǐng)求相關(guān)的值。這些值可能包括用戶身份信息、請(qǐng)求處理日志、跟蹤信息等等)。

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

Context 接口

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

以下是 Context 接口的定義:

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

Context 的核心方法

Context 的核心方法.jpg

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

Deadline()

Deadline() (deadline time.Time, ok bool) 方法返回 Context 的截止時(shí)間,表示在這個(gè)時(shí)間點(diǎn)之后,Context 會(huì)被自動(dòng)取消。如果 Context 沒(méi)有設(shè)置截止時(shí)間,該方法返回一個(gè)零值 time.Time 和一個(gè)布爾值 false。

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

Done()

Done() 方法返回一個(gè)只讀通道,當(dāng) Context 被取消時(shí),該通道會(huì)被關(guān)閉。你可以通過(guò)監(jiān)聽(tīng)這個(gè)通道來(lái)檢測(cè) Context 是否被取消。如果 Context 永不取消,則返回 nil

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

Err()

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

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

Value()

Value(key any) any 方法返回與 Context 關(guān)聯(lián)的鍵值對(duì),一般用于在 Goroutine 之間傳遞請(qǐng)求范圍內(nèi)的信息。如果沒(mé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ù)返回一個(gè)非 nil 的空 Context,它沒(méi)有攜帶任何的值,也沒(méi)有取消和超時(shí)信號(hào)。通常作為根 Context 使用。

ctx := context.Background()

context.TODO()

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

ctx := context.TODO()

context.WithValue()

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

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

context.WithCancel()

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

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

context.WithCancelCause() 與 context.Cause()

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

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

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

err := context.Cause(ctx)

context.WithDeadline()

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

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ù)的功能是一樣的,其底層會(huì)調(diào)用 WithDeadline() 函數(shù),只不過(guò)其第二個(gè)參數(shù)接收的是一個(gè)超時(shí)時(shí)間,而不是截止時(shí)間。這個(gè)函數(shù)適用于需要在一段時(shí)間后取消操作的場(chǎng)景。

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

Context 的使用場(chǎng)景

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

編寫(xiě)中間件函數(shù),用于向 HTTP 處理鏈中添加處理請(qǐng)求 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) {
      // 從請(qǐng)求中提取請(qǐng)求ID和用戶信息
      requestID := req.Header.Get("X-Request-ID")

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

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

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

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

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

啟動(dòng)一個(gè)工作協(xié)程,接收到取消信號(hào)就停止工作。

package main
import (
   "context"
   "fmt"
   "time"
)
func main() {
   ctx, cancelFunc := context.WithCancel(context.Background())
   go Working(ctx)
   time.Sleep(3 * time.Second)
   cancelFunc()
   // 等待一段時(shí)間,以確保工作協(xié)程接收到取消信號(hào)并退出
   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)建了一個(gè) Working 函數(shù),它會(huì)不斷執(zhí)行工作任務(wù)。我們使用 context.WithCancel 創(chuàng)建了一個(gè)上下文 ctx 和一個(gè)取消函數(shù) cancelFunc。然后,啟動(dòng)了一個(gè)工作協(xié)程,并將上下文傳遞給它。

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

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

超時(shí)控制

模擬耗時(shí)操作,超時(shí)控制。

package main

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

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

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

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

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

操作已超時(shí)

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

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

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

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

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

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

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

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

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

小結(jié)

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

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

相關(guān)文章

  • Go語(yǔ)言的隊(duì)列和堆棧實(shí)現(xiàn)方法

    Go語(yǔ)言的隊(duì)列和堆棧實(shí)現(xiàn)方法

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

    在Golang中使用C語(yǔ)言代碼實(shí)例

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

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

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

    使用Go語(yǔ)言提高圖片分辨率的方法與實(shí)踐

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

    Go Excelize API源碼解讀GetSheetViewOptions與SetPageLayo

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

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

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

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

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

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

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

    Go語(yǔ)言庫(kù)系列之flag的具體使用

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

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

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

最新評(píng)論