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

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

 更新時(shí)間:2023年05月17日 14:47:23   作者:Go技術(shù)干貨  
在?Go?的日常開發(fā)中,Context?上下文對(duì)象無處不在,無論是處理網(wǎng)絡(luò)請(qǐng)求、數(shù)據(jù)庫操作還是調(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 的日常開發(fā)中,Context 上下文對(duì)象無處不在,無論是處理網(wǎng)絡(luò)請(qǐng)求、數(shù)據(jù)庫操作還是調(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 沒有設(shè)置截止時(shí)間,該方法返回一個(gè)零值 time.Time 和一個(gè)布爾值 false。

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

Done()

Done() 方法返回一個(gè)只讀通道,當(dāng) Context 被取消時(shí),該通道會(huì)被關(guān)閉。你可以通過監(jiān)聽這個(gè)通道來檢測(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)的信息。如果沒有關(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,它沒有攜帶任何的值,也沒有取消和超時(shí)信號(hào)。通常作為根 Context 使用。

ctx := context.Background()

context.TODO()

context.TODO() 函數(shù)返回一個(gè)非 nil 的空 Context,它沒有攜帶任何的值,也沒有取消和超時(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 版本才新增的,其功能類似于 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。如果是通過 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ù),只不過其第二個(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ù)

編寫中間件函數(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è)處理器。這樣,通過使用 WithRequestId 中間件函數(shù),我們可以在處理請(qǐng)求的過程中方便地獲取和使用請(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ù)庫查詢
      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)。

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

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

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

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

不要在結(jié)構(gòu)類型中加入 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 語言中的 Context 上下文,通過閱讀本文,相信你們對(duì) Context 的功能和使用場(chǎng)景有所了解。同時(shí),你們也應(yīng)該能夠根據(jù)實(shí)際需求選擇最合適的 Context 創(chuàng)建方式,并且根據(jù)規(guī)則,正確、高效地使用它。

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

相關(guān)文章

  • Go微服務(wù)網(wǎng)關(guān)的實(shí)現(xiàn)

    Go微服務(wù)網(wǎng)關(guān)的實(shí)現(xiàn)

    本文主要介紹了Go微服務(wù)網(wǎng)關(guān)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • go各種import的使用方法講解

    go各種import的使用方法講解

    今天小編就為大家分享一篇關(guān)于go各種import的使用方法講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-04-04
  • Golang標(biāo)準(zhǔn)庫unsafe源碼解讀

    Golang標(biāo)準(zhǔn)庫unsafe源碼解讀

    這篇文章主要為大家介紹了Golang標(biāo)準(zhǔn)庫unsafe源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • go-micro微服務(wù)domain層開發(fā)示例詳解

    go-micro微服務(wù)domain層開發(fā)示例詳解

    這篇文章主要為大家介紹了go-micro微服務(wù)domain層開發(fā)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • golang時(shí)間、時(shí)區(qū)、格式的使用方法

    golang時(shí)間、時(shí)區(qū)、格式的使用方法

    這篇文章主要介紹了golang時(shí)間、時(shí)區(qū)、格式的使用方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-04-04
  • 詳解Go是如何優(yōu)雅的進(jìn)行內(nèi)存管理

    詳解Go是如何優(yōu)雅的進(jìn)行內(nèi)存管理

    Go語言拋棄C/C++中的開發(fā)者管理內(nèi)存的方式,實(shí)現(xiàn)了主動(dòng)申請(qǐng)與主動(dòng)釋放管理,增加了逃逸分析和垃圾回收,將開發(fā)者從內(nèi)存管理中釋放出來,作為進(jìn)階的Go開發(fā),了解掌握Go的內(nèi)存管理還是很有必要的
    2023-09-09
  • Go?mod包管理工具詳解

    Go?mod包管理工具詳解

    Go?mod作為Go語言的官方包管理工具,可以幫助開發(fā)者更好地管理包和依賴,提高開發(fā)效率和項(xiàng)目可維護(hù)性,本文將介紹Go語言的包和依賴管理,以及Go?mod的作用和優(yōu)勢(shì),需要的朋友可以參考下
    2023-05-05
  • Golang使用原生http實(shí)現(xiàn)中間件的代碼詳解

    Golang使用原生http實(shí)現(xiàn)中間件的代碼詳解

    中間件(middleware):常被用來做認(rèn)證校驗(yàn)、審計(jì)等,家常用的Iris、Gin等web框架,都包含了中間件邏輯,但有時(shí)我們引入該框架顯得較為繁重,本文將介紹通過golang原生http來實(shí)現(xiàn)中間件操作,需要的朋友可以參考下
    2024-05-05
  • 深入理解?Go?中的字符串

    深入理解?Go?中的字符串

    這篇文章主要介紹了深入理解?Go?中的字符串,在編程語言中,字符串發(fā)揮著重要的角色。字符串背后的數(shù)據(jù)結(jié)構(gòu)一般有兩種類型,一種在編譯時(shí)指定長(zhǎng)度不能修改,一種具有動(dòng)態(tài)的長(zhǎng)度可以修改,下文更多相關(guān)資料需要的小伙伴可以參考一下
    2022-05-05
  • go切片和指針切片示例詳解

    go切片和指針切片示例詳解

    在Go語言中,切片(Slice)和指針的切片(即切片中每個(gè)元素都是指向某種數(shù)據(jù)類型的指針)是兩個(gè)不同的概念,它們各自具有特定的用途和優(yōu)勢(shì),這篇文章主要介紹了go切片和指針切片,需要的朋友可以參考下
    2024-04-04

最新評(píng)論