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

提升Golang應(yīng)用性能:深入理解Context的應(yīng)用

 更新時(shí)間:2023年09月28日 08:46:04   作者:張朝陽(yáng)  
本文將深入探討如何通過(guò)深入理解和正確應(yīng)用Go語(yǔ)言中的Context來(lái)提升應(yīng)用性能。需要的朋友可以參考下

Context本質(zhì)

golang標(biāo)準(zhǔn)庫(kù)里Context實(shí)際上是一個(gè)接口(即一種編程規(guī)范、 一種約定)。

type Context interface {
      Deadline() (deadline time.Time, ok bool)
      Done() <-chan struct{}
      Err() error
      Value(key any) any
}

通過(guò)查看源碼里的注釋?zhuān)覀兊玫饺缦录s定:

  • Done()函數(shù)返回一個(gè)只讀管道,且管道里不存放任何元素(struct{}),所以用這個(gè)管道就是為了實(shí)現(xiàn)阻塞
  • Deadline()用來(lái)記錄到期時(shí)間,以及是否到期。
  • Err()用來(lái)記錄Done()管道關(guān)閉的原因,比如可能是因?yàn)槌瑫r(shí),也可能是因?yàn)楸粡?qiáng)行Cancel了。
  • Value()用來(lái)返回key對(duì)應(yīng)的value,你可以想像成Context內(nèi)部維護(hù)了一個(gè)map。

Context實(shí)現(xiàn)

go源碼里提供了Context接口的一個(gè)具體實(shí)現(xiàn),遺憾的是它只是一個(gè)空的Context,什么也沒(méi)做。

type emptyCtx int
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
    return
}
func (*emptyCtx) Done() <-chan struct{} {
    return nil
}
func (*emptyCtx) Err() error {
    return nil
}
func (*emptyCtx) Value(key any) any {
    return nil
}

emptyCtx以小寫(xiě)開(kāi)頭,包外不可見(jiàn),所以golang又提供了Background和TODO這2個(gè)函數(shù)讓我們能獲取到emptyCtx。

var (
        background = new(emptyCtx)
        todo       = new(emptyCtx)
)
func Background() Context {
        return background
}
func TODO() Context {
        return todo
}

backgroud和todo明明是一模一樣的東西,就是emptyCtx,為什么要搞2個(gè)呢?真心求教,知道的同學(xué)請(qǐng)?jiān)谠u(píng)論區(qū)告訴我。

emptyCtx有什么用?創(chuàng)建Context時(shí)通常需要傳遞一個(gè)父Context,emptyCtx用來(lái)充當(dāng)最初的那個(gè)Root Context。

With Value

當(dāng)業(yè)務(wù)邏輯比較復(fù)雜,函數(shù)調(diào)用鏈很長(zhǎng)時(shí),參數(shù)傳遞會(huì)很復(fù)雜,如下圖:

f1產(chǎn)生的參數(shù)b要傳給f2,雖然f2并不需要參數(shù)b,但f3需要,所以b還是得往后傳。

如果把每一步產(chǎn)生的新變量都放到Context這個(gè)大容器里,函數(shù)之間只傳遞Context,需要什么變量時(shí)直接從Context里取,如下圖:

f2能從context里取到a和b,f4能從context里取到a、b、c、d。

package main
import (
    "context"
    "fmt"
)
func step1(ctx context.Context) context.Context {
    //根據(jù)父context創(chuàng)建子context,創(chuàng)建context時(shí)允許設(shè)置一個(gè)<key,value>對(duì),key和value可以是任意數(shù)據(jù)類(lèi)型
    child := context.WithValue(ctx, "name", "大臉貓")
    return child
}
func step2(ctx context.Context) context.Context {
    fmt.Printf("name %s\n", ctx.Value("name"))
    //子context繼承了父context里的所有key value
    child := context.WithValue(ctx, "age", 18)
    return child
}
func step3(ctx context.Context) {
    fmt.Printf("name %s\n", ctx.Value("name")) //取出key對(duì)應(yīng)的value
    fmt.Printf("age %d\n", ctx.Value("age"))
}
func main1() {
    grandpa := context.Background() //空context
    father := step1(grandpa)        //father里有一對(duì)<key,value>
    grandson := step2(father)       //grandson里有兩對(duì)<key,value>
    step3(grandson)
}

Timeout

在視頻 https://www.bilibili.com/video/BV1C14y127sv/ 里介紹了超時(shí)實(shí)現(xiàn)的核心原理,視頻中演示的done管道可以用Context的Done()來(lái)替代,Context的Done()管道什么時(shí)候會(huì)被關(guān)系呢?2種情況:

1. 通過(guò)context.WithCancel創(chuàng)建一個(gè)context,調(diào)用cancel()時(shí)會(huì)關(guān)閉context.Done()管道。

func f1() {
    ctx, cancel := context.WithCancel(context.Background())
    go func() {
        time.Sleep(100 * time.Millisecond)
        cancel() //調(diào)用cancel,觸發(fā)Done
    }()
    select {
    case <-time.After(300 * time.Millisecond):
        fmt.Println("未超時(shí)")
    case <-ctx.Done(): //ctx.Done()是一個(gè)管道,調(diào)用了cancel()都會(huì)關(guān)閉這個(gè)管道,然后讀操作就會(huì)立即返回
        err := ctx.Err()        //如果發(fā)生Done(管道被關(guān)閉),Err返回Done的原因,可能是被Cancel了,也可能是超時(shí)了
        fmt.Println("超時(shí):", err) //context canceled
    }
}

2. 通過(guò)context.WithTimeout創(chuàng)建一個(gè)context,當(dāng)超過(guò)指定的時(shí)間或者調(diào)用cancel()時(shí)會(huì)關(guān)閉context.Done()管道。

func f2() {
    ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*100) //超時(shí)后會(huì)自動(dòng)調(diào)用context的Deadline,Deadline會(huì),觸發(fā)Done
    defer cancel()
    select {
    case <-time.After(300 * time.Millisecond):
        fmt.Println("未超時(shí)")
    case <-ctx.Done(): //ctx.Done()是一個(gè)管道,context超時(shí)或者調(diào)用了cancel()都會(huì)關(guān)閉這個(gè)管道,然后讀操作就會(huì)立即返回
        err := ctx.Err()        //如果發(fā)生Done(管道被關(guān)閉),Err返回Done的原因,可能是被Cancel了,也可能是超時(shí)了
        fmt.Println("超時(shí):", err) //context deadline exceeded
    }
}

Timeout的繼承問(wèn)題

通過(guò)context.WithTimeout創(chuàng)建的Context,其壽命不會(huì)超過(guò)父Context的壽命。比如:

  • 父Context設(shè)置了10號(hào)到期,5號(hào)誕生了子Context,子Context設(shè)置了100天后到期,則實(shí)際上10號(hào)的時(shí)候子Context也會(huì)到期。
  • 父Context設(shè)置了10號(hào)到期,5號(hào)誕生了子Context,子Context設(shè)置了1天后到期,則實(shí)際上6號(hào)的時(shí)候子Context就會(huì)到期。
func inherit_timeout() {
    parent, cancel1 := context.WithTimeout(context.Background(), time.Millisecond*1000) //parent設(shè)置100ms超時(shí)
    t0 := time.Now()
    defer cancel1()
    time.Sleep(500 * time.Millisecond) //消耗掉500ms
    // child, cancel2 := context.WithTimeout(parent, time.Millisecond*1000) //parent還剩500ms,child設(shè)置了1000ms之后到期,child.Done()管道的關(guān)閉時(shí)刻以較早的為準(zhǔn),即500ms后到期
    child, cancel2 := context.WithTimeout(parent, time.Millisecond*100) //parent還剩500ms,child設(shè)置了100ms之后到期,child.Done()管道的關(guān)閉時(shí)刻以較早的為準(zhǔn),即100ms后到期
    t1 := time.Now()
    defer cancel2()
    select {
    case <-child.Done():
        t2 := time.Now()
        fmt.Println(t2.Sub(t0).Milliseconds(), t2.Sub(t1).Milliseconds())
        fmt.Println(child.Err()) //context deadline exceeded
    }
}

context超時(shí)在http請(qǐng)求中的實(shí)際應(yīng)用

定心丸來(lái)了,最后說(shuō)一遍:”context在實(shí)踐中真的很有用“

客戶端發(fā)起http請(qǐng)求時(shí)設(shè)置了一個(gè)2秒的超時(shí)時(shí)間:

package main
import (
    "fmt"
    "io/ioutil"
    "net/http"
    "time"
)
func main() {
    client := http.Client{
        Timeout: 2 * time.Second, //小于10秒,導(dǎo)致請(qǐng)求超時(shí),會(huì)觸發(fā)Server端的http.Request.Context的Done
    }
    if resp, err := client.Get("http://127.0.0.1:5678/"); err == nil {
        defer resp.Body.Close()
        fmt.Println(resp.StatusCode)
        if bs, err := ioutil.ReadAll(resp.Body); err == nil {
            fmt.Println(string(bs))
        }
    } else {
        fmt.Println(err) //Get "http://127.0.0.1:5678/": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
    }
}

服務(wù)端從Request里取提context,故意休息10秒鐘,同時(shí)監(jiān)聽(tīng)context.Done()管道有沒(méi)有關(guān)閉。由于Request的context是2秒超時(shí),所以服務(wù)端還沒(méi)休息夠context.Done()管道就關(guān)閉了。

package main
import (
    "fmt"
    "net/http"
    "time"
)
func welcome(w http.ResponseWriter, req *http.Request) {
    ctx := req.Context() //取得request的context
    select {
    case <-time.After(10 * time.Second): //故意慢一點(diǎn),10秒后才返回結(jié)果
        fmt.Fprintf(w, "welcome")
    case <-ctx.Done(): //超時(shí)后client會(huì)撤銷(xiāo)請(qǐng)求,觸發(fā)ctx.cancel(),從而關(guān)閉Done()管道
        err := ctx.Err()            //如果發(fā)生Done(管道被關(guān)閉),Err返回Done的原因,可能是被Cancel了,也可能是超時(shí)了
        fmt.Println("server:", err) //context canceled
    }
}
func main() {
    http.HandleFunc("/", welcome)
    http.ListenAndServe(":5678", nil)
}

本文深入探討了Go語(yǔ)言中Context的應(yīng)用,以提升應(yīng)用性能。我們首先介紹了Context的基本概念和用法,包括創(chuàng)建和傳遞Context、超時(shí)和取消機(jī)制等。然后,我們討論了如何正確使用Context,避免誤用和濫用。接著,我們探討了如何利用Context優(yōu)化并發(fā)和并行操作,以及處理請(qǐng)求鏈中的多個(gè)Context。通過(guò)深入理解和正確應(yīng)用Context,開(kāi)發(fā)者可以更好地管理資源、控制并發(fā)和處理請(qǐng)求鏈,從而提升應(yīng)用的性能和可靠性。建議開(kāi)發(fā)者在開(kāi)發(fā)Golang應(yīng)用時(shí)充分利用Context,并遵循本文提供的最佳實(shí)踐,以獲得更好的應(yīng)用性能和用戶體驗(yàn)。

到此這篇關(guān)于提升Golang應(yīng)用性能:深入理解Context的應(yīng)用的文章就介紹到這了,更多相關(guān)golang Context應(yīng)用舉例內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • go項(xiàng)目打包部署的完整步驟

    go項(xiàng)目打包部署的完整步驟

    之前斷斷續(xù)續(xù)的接觸到項(xiàng)目部署,一直沒(méi)有詳細(xì)的了解部署,于是最近就好好的專(zhuān)研一下項(xiàng)目的部署,下面這篇文章主要給大家介紹了關(guān)于go項(xiàng)目打包部署的相關(guān)資料,需要的朋友可以參考下
    2022-09-09
  • Go語(yǔ)言狀態(tài)機(jī)的實(shí)現(xiàn)

    Go語(yǔ)言狀態(tài)機(jī)的實(shí)現(xiàn)

    本文主要介紹了Go語(yǔ)言狀態(tài)機(jī)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • Golang在Window環(huán)境使用Imagick7的過(guò)程

    Golang在Window環(huán)境使用Imagick7的過(guò)程

    這篇文章主要介紹了Golang在Window環(huán)境使用Imagick7的過(guò)程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2023-11-11
  • Go語(yǔ)言reflect.TypeOf()和reflect.Type通過(guò)反射獲取類(lèi)型信息

    Go語(yǔ)言reflect.TypeOf()和reflect.Type通過(guò)反射獲取類(lèi)型信息

    這篇文章主要介紹了Go語(yǔ)言reflect.TypeOf()和reflect.Type通過(guò)反射獲取類(lèi)型信息,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • go語(yǔ)言實(shí)現(xiàn)markdown解析庫(kù)的方法示例

    go語(yǔ)言實(shí)現(xiàn)markdown解析庫(kù)的方法示例

    這篇文章主要介紹了go語(yǔ)言實(shí)現(xiàn)markdown解析庫(kù)的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • Go語(yǔ)言Select chan用法小結(jié)

    Go語(yǔ)言Select chan用法小結(jié)

    select語(yǔ)句是Go語(yǔ)言中用于處理多個(gè)通道操作的關(guān)鍵字,它允許你在多個(gè)通道上進(jìn)行非阻塞的選擇操作,本文就詳細(xì)介紹一下如何使用,感興趣的可以了解一下
    2023-09-09
  • 一文帶你掌握Go語(yǔ)言并發(fā)模式中的Context的上下文管理

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

    在?Go?的日常開(kāi)發(fā)中,Context?上下文對(duì)象無(wú)處不在,無(wú)論是處理網(wǎng)絡(luò)請(qǐng)求、數(shù)據(jù)庫(kù)操作還是調(diào)用?RPC?等場(chǎng)景,那你真的熟悉它的正確用法嗎,隨著本文一探究竟吧
    2023-05-05
  • golang如何判斷字符串是否包含中文

    golang如何判斷字符串是否包含中文

    這篇文章主要介紹了golang如何判斷字符串是否包含中文問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-07-07
  • Golang單元測(cè)試與覆蓋率的實(shí)例講解

    Golang單元測(cè)試與覆蓋率的實(shí)例講解

    這篇文章主要介紹了Golang單元測(cè)試與覆蓋率的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-11-11
  • Golang實(shí)現(xiàn)簡(jiǎn)單http服務(wù)器的示例詳解

    Golang實(shí)現(xiàn)簡(jiǎn)單http服務(wù)器的示例詳解

    這篇文章主要為大家詳細(xì)介紹了如何利用Golang實(shí)現(xiàn)簡(jiǎn)單http服務(wù)器,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Golang有一定的幫助,需要的可以參考一下
    2023-03-03

最新評(píng)論

衡东县| 黎川县| 区。| 肇东市| 山阳县| 塔河县| 宝兴县| 建水县| 汽车| 思茅市| 安国市| 滕州市| 玉山县| 万载县| 临泉县| 清河县| 潼关县| 新津县| 安仁县| 宁乡县| 呼和浩特市| 贡嘎县| 察哈| 电白县| 泗水县| 通许县| 崇文区| 沾益县| 东丰县| 江达县| 临澧县| 深州市| 鹰潭市| 长武县| 铅山县| 五大连池市| 土默特左旗| 噶尔县| 苗栗县| 龙里县| 榆中县|