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

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

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

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

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

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

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

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

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

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

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

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

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 給新開(kāi)的 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)行代碼,在瀏覽器中訪問(wèn) http://127.0.0.1:8080/test,控制臺(tái)會(huì)打印如下錯(cuò)誤信息:

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

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

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

這種函數(shù)該如何實(shí)現(xiàn)呢?其實(shí) Golang 從 1.21 版本開(kāi)始為我們提供了這樣一個(gè)函數(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"
}

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

Go 版本低于 1.21 該怎么辦

如果 Go 版本低于 1.21 其實(shí)也很好辦,按照 Go 1.21 中的實(shí)現(xiàn)方式自己實(shí)現(xiàn)一個(gè)就可以了,代碼可以進(jìn)一步精簡(jiǎ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
}

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

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 給新開(kāi)的 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)行代碼,在瀏覽器中訪問(wèn) http://127.0.0.1:8080/test,發(fā)現(xiàn)不再報(bào)父 context 被 cancel 導(dǎo)致的報(bào)錯(cuò)了。

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

相關(guān)文章

  • Golang實(shí)現(xiàn)Directional Channel(定向通道)

    Golang實(shí)現(xiàn)Directional Channel(定向通道)

    這篇文章主要介紹了Golang實(shí)現(xiàn)Directional Channel(定向通道),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • Go語(yǔ)言之init函數(shù)

    Go語(yǔ)言之init函數(shù)

    Go語(yǔ)言有一個(gè)特殊的函數(shù)init,先于main函數(shù)執(zhí)行,實(shí)現(xiàn)包級(jí)別的一些初始化操作。這篇文章介紹了Go中的Init函數(shù),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • Go語(yǔ)言調(diào)用ffmpeg-api實(shí)現(xiàn)音頻重采樣

    Go語(yǔ)言調(diào)用ffmpeg-api實(shí)現(xiàn)音頻重采樣

    最近對(duì)golang處理音視頻很感興趣,對(duì)golang音視頻常用庫(kù)goav進(jìn)行了一番研究。自己寫(xiě)了一個(gè)wav轉(zhuǎn)采樣率的功能。給大家分享一下,中間遇到了不少坑,解決的過(guò)程中還是蠻有意思的,希望大家能喜歡
    2022-12-12
  • Golong字符串拼接性能優(yōu)化及原理介紹

    Golong字符串拼接性能優(yōu)化及原理介紹

    最近在做性能優(yōu)化,有個(gè)函數(shù)里面的耗時(shí)特別長(zhǎng),看里面的操作大多是一些字符串拼接的操作,而字符串拼接在 golang 里面其實(shí)有很多種實(shí)現(xiàn),下面這篇文章主要給大家介紹了關(guān)于Golang語(yǔ)言如何高效拼接字符串的相關(guān)資料,需要的朋友可以參考下
    2023-04-04
  • Go語(yǔ)言中的switch用法實(shí)例分析

    Go語(yǔ)言中的switch用法實(shí)例分析

    這篇文章主要介紹了Go語(yǔ)言中的switch用法,實(shí)例分析了switch的功能及使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • goland 清除所有的默認(rèn)設(shè)置操作

    goland 清除所有的默認(rèn)設(shè)置操作

    這篇文章主要介紹了goland 清除所有的默認(rèn)設(shè)置操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • Go語(yǔ)言通過(guò)http抓取網(wǎng)頁(yè)的方法

    Go語(yǔ)言通過(guò)http抓取網(wǎng)頁(yè)的方法

    這篇文章主要介紹了Go語(yǔ)言通過(guò)http抓取網(wǎng)頁(yè)的方法,實(shí)例分析了Go語(yǔ)言通過(guò)http操作頁(yè)面的技巧,需要的朋友可以參考下
    2015-03-03
  • 使用go求冪的幾種方法小結(jié)

    使用go求冪的幾種方法小結(jié)

    這篇文章主要介紹了使用go求冪的幾種方法小結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • Go高效率開(kāi)發(fā)Web參數(shù)校驗(yàn)三種方式實(shí)例

    Go高效率開(kāi)發(fā)Web參數(shù)校驗(yàn)三種方式實(shí)例

    這篇文章主要介紹了Go高效率開(kāi)發(fā)Web參數(shù)校驗(yàn)三種方式實(shí)例,需要的朋友可以參考下
    2022-11-11
  • Gin+Gorm實(shí)現(xiàn)增刪改查的示例代碼

    Gin+Gorm實(shí)現(xiàn)增刪改查的示例代碼

    本文介紹了如何使用Gin和Gorm框架實(shí)現(xiàn)一個(gè)簡(jiǎn)單的增刪改查(CRUD)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-12-12

最新評(píng)論