Go 語言 context 都能做什麼?

来源:https://www.cnblogs.com/alwaysbeta/archive/2023/07/02/17520650.html
-Advertisement-
Play Games

**原文鏈接:** [Go 語言 context 都能做什麼?](https://mp.weixin.qq.com/s/7IliODEUt3JpEuzL8K_sOg) 很多 Go 項目的源碼,在讀的過程中會發現一個很常見的參數 `ctx`,而且基本都是作為函數的第一個參數。 為什麼要這麼寫呢?這個參 ...


原文鏈接: Go 語言 context 都能做什麼?

很多 Go 項目的源碼,在讀的過程中會發現一個很常見的參數 ctx,而且基本都是作為函數的第一個參數。

為什麼要這麼寫呢?這個參數到底有什麼用呢?帶著這樣的疑問,我研究了這個參數背後的故事。

開局一張圖:

核心是 Context 介面:

// A Context carries a deadline, cancelation signal, and request-scoped values
// across API boundaries. Its methods are safe for simultaneous use by multiple
// goroutines.
type Context interface {
    // Done returns a channel that is closed when this Context is canceled
    // or times out.
    Done() <-chan struct{}

    // Err indicates why this context was canceled, after the Done channel
    // is closed.
    Err() error

    // Deadline returns the time when this Context will be canceled, if any.
    Deadline() (deadline time.Time, ok bool)

    // Value returns the value associated with key or nil if none.
    Value(key interface{}) interface{}
}

包含四個方法:

  • Done():返回一個 channel,當 times out 或者調用 cancel 方法時。
  • Err():返回一個錯誤,表示取消 ctx 的原因。
  • Deadline():返回截止時間和一個 bool 值。
  • Value():返回 key 對應的值。

有四個結構體實現了這個介面,分別是:emptyCtx, cancelCtx, timerCtxvalueCtx

其中 emptyCtx 是空類型,暴露了兩個方法:

func Background() Context
func TODO() Context

一般情況下,會使用 Background() 作為根 ctx,然後在其基礎上再派生出子 ctx。要是不確定使用哪個 ctx,就使用 TODO()

另外三個也分別暴露了對應的方法:

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context

遵循規則

在使用 Context 時,要遵循以下四點規則:

  1. 不要將 Context 放入結構體,而是應該作為第一個參數傳入,命名為 ctx
  2. 即使函數允許,也不要傳入 nil 的 Context。如果不知道用哪種 Context,可以使用 context.TODO()
  3. 使用 Context 的 Value 相關方法只應該用於在程式和介面中傳遞和請求相關的元數據,不要用它來傳遞一些可選的參數。
  4. 相同的 Context 可以傳遞給不同的 goroutine;Context 是併發安全的。

WithCancel

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

WithCancel 返回帶有新 Done 通道的父級副本。當調用返回的 cancel 函數或關閉父上下文的 Done 通道時,返回的 ctxDone 通道將關閉。

取消此上下文會釋放與其關聯的資源,因此在此上下文中運行的操作完成後,代碼應立即調用 cancel

舉個例子:

這段代碼演示瞭如何使用可取消上下文來防止 goroutine 泄漏。在函數結束時,由 gen 啟動的 goroutine 將返回而不會泄漏。

package main

import (
    "context"
    "fmt"
)

func main() {
    // gen generates integers in a separate goroutine and
    // sends them to the returned channel.
    // The callers of gen need to cancel the context once
    // they are done consuming generated integers not to leak
    // the internal goroutine started by gen.
    gen := func(ctx context.Context) <-chan int {
        dst := make(chan int)
        n := 1
        go func() {
            for {
                select {
                case <-ctx.Done():
                    return // returning not to leak the goroutine
                case dst <- n:
                    n++
                }
            }
        }()
        return dst
    }

    ctx, cancel := context.WithCancel(context.Background())
    defer cancel() // cancel when we are finished consuming integers

    for n := range gen(ctx) {
        fmt.Println(n)
        if n == 5 {
            break
        }
    }
}

輸出:

1
2
3
4
5

WithDeadline

func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)

WithDeadline 返回父上下文的副本,並將截止日期調整為不晚於 d。如果父級的截止日期已經早於 d,則 WithDeadline(parent, d) 在語義上等同於 parent

當截止時間到期、調用返回的取消函數時或當父上下文的 Done 通道關閉時,返回的上下文的 Done 通道將關閉。

取消此上下文會釋放與其關聯的資源,因此在此上下文中運行的操作完成後,代碼應立即調用取消。

舉個例子:

這段代碼傳遞具有截止時間的上下文,來告訴阻塞函數,它應該在到達截止時間時立刻退出。

package main

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

const shortDuration = 1 * time.Millisecond

func main() {
    d := time.Now().Add(shortDuration)
    ctx, cancel := context.WithDeadline(context.Background(), d)

    // Even though ctx will be expired, it is good practice to call its
    // cancellation function in any case. Failure to do so may keep the
    // context and its parent alive longer than necessary.
    defer cancel()

    select {
    case <-time.After(1 * time.Second):
        fmt.Println("overslept")
    case <-ctx.Done():
        fmt.Println(ctx.Err())
    }
}

輸出:

context deadline exceeded

WithTimeout

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)

WithTimeout 返回 WithDeadline(parent, time.Now().Add(timeout))

取消此上下文會釋放與其關聯的資源,因此在此上下文中運行的操作完成後,代碼應立即調用取消。

舉個例子:

這段代碼傳遞帶有超時的上下文,以告訴阻塞函數應在超時後退出。

package main

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

const shortDuration = 1 * time.Millisecond

func main() {
    // Pass a context with a timeout to tell a blocking function that it
    // should abandon its work after the timeout elapses.
    ctx, cancel := context.WithTimeout(context.Background(), shortDuration)
    defer cancel()

    select {
    case <-time.After(1 * time.Second):
        fmt.Println("overslept")
    case <-ctx.Done():
        fmt.Println(ctx.Err()) // prints "context deadline exceeded"
    }

}

輸出:

context deadline exceeded

WithValue

func WithValue(parent Context, key, val any) Context

WithValue 返回父級的副本,其中與 key 關聯的值為 val

其中鍵必須是可比較的,並且不應是字元串類型或任何其他內置類型,以避免使用上下文的包之間發生衝突。 WithValue 的用戶應該定義自己的鍵類型。

為了避免分配給 interface{},上下文鍵通常具有具體的 struct{} 類型。或者,導出的上下文鍵變數的靜態類型應該是指針或介面。

舉個例子:

這段代碼演示瞭如何將值傳遞到上下文以及如何檢索它(如果存在)。

package main

import (
    "context"
    "fmt"
)

func main() {
    type favContextKey string

    f := func(ctx context.Context, k favContextKey) {
        if v := ctx.Value(k); v != nil {
            fmt.Println("found value:", v)
            return
        }
        fmt.Println("key not found:", k)
    }

    k := favContextKey("language")
    ctx := context.WithValue(context.Background(), k, "Go")

    f(ctx, k)
    f(ctx, favContextKey("color"))
}

輸出:

found value: Go
key not found: color

本文的大部分內容,包括代碼示例都是翻譯自官方文檔,代碼都是經過驗證可以執行的。如果有不是特別清晰的地方,可以直接去讀官方文檔。

以上就是本文的全部內容,如果覺得還不錯的話歡迎點贊轉發關註,感謝支持。


官方文檔:

源碼分析:

推薦閱讀:


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 關鍵字 abstractassertbooleanbreakbyte case catch char class const continue default do double else enum extends final finally float for goto if implementi ...
  • 不說廢話,直接上乾貨: (註意大小寫:object為對象,Object為類) 1,object.getClass()它是Object類的實例方法,返回一個對象運行時的類的Class對象,換句話說,它返回的是對象具體類型的類對象。 2,Object.class 這是java語言的一種語法糖,用來返回一 ...
  • # 前言 最近針對java項目的部署方式進行整理,jenkins/tomcat/windows工具/linux腳本/web部署平臺等等 發現war包通過tomcat部署比較繁瑣,等待時間長,配置規則複雜對於小白很不友好,也難以接入到自定義的部署工具/平臺中 之前開發的Jar包部署平臺是servlet ...
  • 數組索引是指在`numpy`數組中引用特定元素的方法。`numpy`的數組索引又稱為`fancy indexing`,比其他編程語言的索引強大很多。 # 1. 選取數據 numpy的索引除了像其他語言一樣選擇一個元素,還可以間隔著選取多個元素,也可以用任意的順序選取元素。 比如一維數組: ```py ...
  • urllib+BeautifulSoup爬取並解析2345天氣王歷史天氣數據 網址:[東城歷史天氣查詢_歷史天氣預報查詢_2345天氣預報](https://tianqi.2345.com/wea_history/71445.htm) ![image-20230702161423470](https ...
  • # 數列分段 Section II ## 題目描述 對於給定的一個長度為N的正整數數列 $A_{1\sim N}$,現要將其分成 $M$($M\leq N$)段,並要求每段連續,且每段和的最大值最小。 關於最大值最小: 例如一數列 $4\ 2\ 4\ 5\ 1$ 要分成 $3$ 段。 將其如下分段: ...
  • 本文通過閱讀Spring源碼,分析Bean實例化流程。 # Bean實例化入口 上一篇文章已經介紹,Bean實例化入口在AbstractApplicationContext類的finishBeanFactoryInitialization方法: ```java protected void fini ...
  • # 1、Java常用插件實現方案 ## 1.2、serviceloader方式 serviceloader是java提供的spi模式的實現。按照介面開發實現類,而後配置,java通過ServiceLoader來實現統一介面不同實現的依次調用。而java中最經典的serviceloader的使用就是J ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...