最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Go如何基于現(xiàn)有的context創(chuàng)建新的context

 更新時間:2024年01月17日 08:37:07   作者:路多辛  
在?Golang?中,context?包提供了創(chuàng)建和管理上下文的功能,那么在GO語言中如何基于現(xiàn)有的context創(chuàng)建新的context,下面小編就來和大家詳細(xì)聊聊

在 Golang 中,context 包提供了創(chuàng)建和管理上下文的功能。當(dāng)需要基于現(xiàn)有的 context.Context 創(chuàng)建新的 context 時,通常是為了添加額外的控制信息或為了滿足特定的生命周期需求。

基于現(xiàn)有的 context 創(chuàng)建新的 context

可以基于現(xiàn)有的 context.Context 創(chuàng)建一個新的 context,對應(yīng)的函數(shù)有 context.WithCancel、context.WithDeadline、context.WithTimeout 或 context.WithValue。這些函數(shù)會返回一個新的 context.Context 實例,繼承了原來 context 的行為,并添加了新的行為或值。使用 context.WithValue 函數(shù)創(chuàng)建的簡單示例代碼如下:

package main
 
import "context"
 
func main() {
    // 假設(shè)已經(jīng)有了一個context ctx
    ctx := context.Background()
    // 可以通過context.WithValue創(chuàng)建一個新的context
    key := "myKey"
    value := "myValue"
    newCtx := context.WithValue(ctx, key, value)
    // 現(xiàn)在newCtx包含了原始ctx的所有數(shù)據(jù),加上新添加的鍵值對
}

使用 context.WithCancel 函數(shù)創(chuàng)建,簡單示例代碼如下:

package main
 
import "context"
 
func main() {
    // 假設(shè)已經(jīng)有了一個context ctx
    ctx := context.Background()
    // 創(chuàng)建一個可取消的context
    newCtx, cancel := context.WithCancel(ctx)
    // 當(dāng)完成了newCtx的使用,可以調(diào)用cancel來取消它
    // 這將釋放與該context相關(guān)的資源
    defer cancel()
}

現(xiàn)有創(chuàng)建方法的問題

先說一個使用場景:一個接口處理完基本的任務(wù)之后,后續(xù)一些處理的任務(wù)放使用新開的 Goroutine 來處理,這時候會基于當(dāng)前的 context 創(chuàng)建一個 context(可以使用上面提到的方法來創(chuàng)建) 給 Goroutine 使用,也不需要控制 Goroutine 的超時時間。

這種場景下,Goroutine 的聲明周期一般都會比這個接口的生命周期長,這就會出現(xiàn)一個問題——當(dāng)前接口請求所屬的 Goroutine 退出后會導(dǎo)致 context 被 cancel,進(jìn)而導(dǎo)致新開的 Goroutine 中的 context 跟著被 cancel, 從而導(dǎo)致程序異常??匆粋€示例:

package main
 
import (
    "bytes"
    "context"
    "errors"
    "fmt"
    "io"
    "net/http"
 
    "github.com/gin-gonic/gin"
)
 
func main() {
    r := gin.New()
    r.GET("/test", func(c *gin.Context) {
       // 父 context,有使用取消功能
       ctx, cancel := context.WithCancel(c)
       defer cancel()
 
       // 創(chuàng)建子 context 給新開的 Goroutine 使用
       ctxCopy, _ := context.WithCancel(ctx)
       go func() {
          err := TestPost(ctxCopy)
          fmt.Println(err)
       }()
    })
    r.Run(":8080")
}
 
func TestPost(ctx context.Context) error {
    fmt.Println("goroutine...")
    buffer := bytes.NewBuffer([]byte(`{"xxx":"xxx"}`))
    request, err := http.NewRequest("POST", "http://xxx.luduoxin.com/xxx", buffer)
    if err != nil {
       return err
    }
    request.Header.Set("Content-Type", "application/json")
    client := http.Client{}
    rsp, err := client.Do(request.WithContext(ctx))
    if err != nil {
       return err
    }
    defer func() {
       _ = rsp.Body.Close()
    }()
    if rsp.StatusCode != http.StatusOK {
       return errors.New("response exception")
    }
    _, err = io.ReadAll(rsp.Body)
    if err != nil {
       return err
    }
    return nil
}

運(yùn)行代碼,在瀏覽器中訪問 http://127.0.0.1:8080/test,控制臺會打印如下錯誤信息:

goroutine...
Post "http://xxx.luduoxin.com/xxx": context canceled

可以看出,因為父級 context 被 cancel,導(dǎo)致子 context 也被 cancel,從而導(dǎo)致程序異常。因此,需要一種既能繼承父 context 所有的 value 信息,又能去除父級 context 的 cancel 機(jī)制的創(chuàng)建函數(shù)。

Go 1.21 中的 context.WithoutCancel 函數(shù)

這種函數(shù)該如何實現(xiàn)呢?其實 Golang 從 1.21 版本開始為我們提供了這樣一個函數(shù),就是 context 包中的 WithoutCancel 函數(shù)。源代碼如下:

func WithoutCancel(parent Context) Context {
    if parent == nil {
       panic("cannot create context from nil parent")
    }
    return withoutCancelCtx{parent}
}
 
type withoutCancelCtx struct {
    c Context
}
 
func (withoutCancelCtx) Deadline() (deadline time.Time, ok bool) {
    return
}
 
func (withoutCancelCtx) Done() <-chan struct{} {
    return nil
}
 
func (withoutCancelCtx) Err() error {
    return nil
}
 
func (c withoutCancelCtx) Value(key any) any {
    return value(c, key)
}
 
func (c withoutCancelCtx) String() string {
    return contextName(c.c) + ".WithoutCancel"
}

原理其實很簡單,主要功能是創(chuàng)建一個新的 context 類型,繼承了父 context 的所有屬性,但重寫了 Deadline、Done、Err、Value 幾個方法,當(dāng)父 context 被取消時不會觸發(fā)任何操作。

Go 版本低于 1.21 該怎么辦

如果 Go 版本低于 1.21 其實也很好辦,按照 Go 1.21 中的實現(xiàn)方式自己實現(xiàn)一個就可以了,代碼可以進(jìn)一步精簡,示例代碼如下:

func WithoutCancel(parent Context) Context {
    if parent == nil {
       panic("cannot create context from nil parent")
    }
    return withoutCancelCtx{parent}
}
 
type withoutCancelCtx struct {
    context.Context
}
 
func (withoutCancelCtx) Deadline() (deadline time.Time, ok bool) {
    return
}
 
func (withoutCancelCtx) Done() <-chan struct{} {
    return nil
}
 
func (withoutCancelCtx) Err() error {
    return nil
}

使用自己實現(xiàn)的這個版本再跑一下之前的示例,代碼如下:

package main
 
import (
    "bytes"
    "context"
    "errors"
    "fmt"
    "io"
    "net/http"
    "time"
 
    "github.com/gin-gonic/gin"
)
 
func main() {
    r := gin.New()
    r.GET("/test", func(c *gin.Context) {
       // 父 context,有使用取消功能
       ctx, cancel := context.WithCancel(c)
       defer cancel()
 
       // 創(chuàng)建子 context 給新開的 Goroutine 使用
       ctxCopy := WithoutCancel(ctx)
       go func() {
          err := TestPost(ctxCopy)
          fmt.Println(err)
       }()
    })
    r.Run(":8080")
}
 
func WithoutCancel(parent Context) Context {
    if parent == nil {
       panic("cannot create context from nil parent")
    }
    return withoutCancelCtx{parent}
}
 
type withoutCancelCtx struct {
    context.Context
}
 
func (withoutCancelCtx) Deadline() (deadline time.Time, ok bool) {
    return
}
 
func (withoutCancelCtx) Done() <-chan struct{} {
    return nil
}
 
func (withoutCancelCtx) Err() error {
    return nil
}
 
func TestPost(ctx context.Context) error {
    fmt.Println("goroutine...")
    buffer := bytes.NewBuffer([]byte(`{"xxx":"xxx"}`))
    request, err := http.NewRequest("POST", "http://xxx.luduoxin.com/xxx", buffer)
    if err != nil {
       return err
    }
    request.Header.Set("Content-Type", "application/json")
    client := http.Client{}
    rsp, err := client.Do(request.WithContext(ctx))
    if err != nil {
       return err
    }
    defer func() {
       _ = rsp.Body.Close()
    }()
    if rsp.StatusCode != http.StatusOK {
       return errors.New("response exception")
    }
    _, err = io.ReadAll(rsp.Body)
    if err != nil {
       return err
    }
    return nil
}
 
type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key any) any
}

運(yùn)行代碼,在瀏覽器中訪問 http://127.0.0.1:8080/test,發(fā)現(xiàn)不再報父 context 被 cancel 導(dǎo)致的報錯了。

以上就是詳解Go如何基于現(xiàn)有的context創(chuàng)建新的context的詳細(xì)內(nèi)容,更多關(guān)于Go context的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 一文了解Go語言io.Copy函數(shù)

    一文了解Go語言io.Copy函數(shù)

    這篇文章主要為大家介紹了Go語言io.Copy函數(shù)使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • golang程序打包成腳本部署到Linux系統(tǒng)方式

    golang程序打包成腳本部署到Linux系統(tǒng)方式

    Golang程序通過本地編譯(設(shè)置GOOS為linux生成無后綴二進(jìn)制文件),上傳至Linux服務(wù)器后賦權(quán)執(zhí)行,使用nohup命令實現(xiàn)后臺運(yùn)行,完成自動化部署
    2025-07-07
  • Go語言sync.Once和sync.Cond的實現(xiàn)

    Go語言sync.Once和sync.Cond的實現(xiàn)

    本文主要介紹了Go語言sync.Once和sync.Cond的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-07-07
  • Golang實現(xiàn)http server提供壓縮文件下載功能

    Golang實現(xiàn)http server提供壓縮文件下載功能

    這篇文章主要介紹了Golang實現(xiàn)http server提供壓縮文件下載功能,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • Go語言zip文件的讀寫操作

    Go語言zip文件的讀寫操作

    本文主要介紹了Go語言zip文件的讀寫操作,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • 淺析Go中序列化與反序列化的基本使用

    淺析Go中序列化與反序列化的基本使用

    序列化是指將對象轉(zhuǎn)換成字節(jié)流,從而存儲對象或?qū)ο髠鬏數(shù)絻?nèi)存、數(shù)據(jù)庫或文件的過程,反向過程稱為“反序列化”。本文主要介紹了Go中序列化與反序列化的基本使用,需要的可以參考一下
    2023-04-04
  • Golang使用Consul詳解

    Golang使用Consul詳解

    Consul是一個服務(wù)發(fā)現(xiàn)軟件, 提供了服務(wù)發(fā)現(xiàn)\鍵值存儲\健康檢查等功能,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • Go接口構(gòu)建可擴(kuò)展Go應(yīng)用示例詳解

    Go接口構(gòu)建可擴(kuò)展Go應(yīng)用示例詳解

    本文深入探討了Go語言中接口的概念和實際應(yīng)用場景。從基礎(chǔ)知識如接口的定義和實現(xiàn),到更復(fù)雜的實戰(zhàn)應(yīng)用如解耦與抽象、多態(tài)、錯誤處理、插件架構(gòu)以及資源管理,文章通過豐富的代碼示例和詳細(xì)的解釋,展示了Go接口在軟件開發(fā)中的強(qiáng)大功能和靈活性
    2023-10-10
  • Go中map數(shù)據(jù)類型3點小知識

    Go中map數(shù)據(jù)類型3點小知識

    這篇文章主要介紹了Go中map數(shù)據(jù)類型3點小知識,在go中使用map時可能會用本文中的小知識,需要的朋友可以參考下
    2014-10-10
  • Golang實現(xiàn)讀取excel文件并轉(zhuǎn)換為JSON格式

    Golang實現(xiàn)讀取excel文件并轉(zhuǎn)換為JSON格式

    本文介紹了如何使用Golang讀取Excel文件并將其轉(zhuǎn)換為JSON格式,通過安裝excelize依賴和創(chuàng)建readExcelToJSON方法,可以實現(xiàn)這一功能,如果需要轉(zhuǎn)換數(shù)據(jù)類型,可以修改相應(yīng)的代碼,需要的朋友可以參考下
    2025-03-03

最新評論

高雄县| 闵行区| 景德镇市| 长白| 巨野县| 夏邑县| 婺源县| 湛江市| 江津市| 随州市| 光山县| 淮阳县| 霍林郭勒市| 吴旗县| 高台县| 西华县| 海兴县| 华蓥市| 晋江市| 高尔夫| 平江县| 永年县| 大邑县| 巴林左旗| 定边县| 望江县| 从江县| 集安市| 昌宁县| 定远县| 苍溪县| 外汇| 东明县| 萨嘎县| 台安县| 灵川县| 古浪县| 鄂伦春自治旗| 津南区| 阿荣旗| 砀山县|