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

詳解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 時,通常是為了添加額外的控制信息或?yàn)榱藵M足特定的生命周期需求。

基于現(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 實(shí)例,繼承了原來 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

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

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

這種函數(shù)該如何實(shí)現(xiàn)呢?其實(shí) 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"
}

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

Go 版本低于 1.21 該怎么辦

如果 Go 版本低于 1.21 其實(shí)也很好辦,按照 Go 1.21 中的實(shí)現(xiàn)方式自己實(shí)現(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
}

使用自己實(shí)現(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)不再報(bào)父 context 被 cancel 導(dǎo)致的報(bào)錯了。

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

相關(guān)文章

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

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

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

    Go語言之init函數(shù)

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

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

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

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

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

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

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

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

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

    Go語言通過http抓取網(wǎng)頁的方法

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

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

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

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

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

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

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

最新評論