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

Go語言context上下文管理的使用

 更新時間:2022年03月09日 11:27:24   作者:golandscape  
本文主要介紹了Go語言context上下文管理的使用,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下

context 有什么作用

context 主要用來在goroutine 之間傳遞上下文信息,包括:取消信號、超時時間、截止時間、k-v 等。

Go 常用來寫后臺服務(wù),通常只需要幾行代碼,就可以搭建一個 http server。

在 Go 的 server 里,通常每來一個請求都會啟動若干個 goroutine 同時工作:有些去數(shù)據(jù)庫拿數(shù)據(jù),有些調(diào)用下游接口獲取相關(guān)數(shù)據(jù)……

這些 goroutine 需要共享這個請求的基本數(shù)據(jù),例如登陸的 token,處理請求的最大超時時間(如果超過此值再返回數(shù)據(jù),請求方因為超時接收不到)等等。當(dāng)請求被取消或是處理時間太長,這有可能是使用者關(guān)閉了瀏覽器或是已經(jīng)超過了請求方規(guī)定的超時時間,請求方直接放棄了這次請求結(jié)果。這時,所有正在為這個請求工作的 goroutine 需要快速退出,因為它們的“工作成果”不再被需要了。在相關(guān)聯(lián)的 goroutine 都退出后,系統(tǒng)就可以回收相關(guān)的資源。

在Go 里,我們不能直接殺死協(xié)程,協(xié)程的關(guān)閉一般會用 channel+select 方式來控制。但是在某些場景下,例如處理一個請求衍生了很多協(xié)程,這些協(xié)程之間是相互關(guān)聯(lián)的:需要共享一些全局變量、有共同的 deadline 等,而且可以同時被關(guān)閉。再用 channel+select 就會比較麻煩,這時就可以通過 context 來實現(xiàn)。

一句話:context 用來解決 goroutine 之間退出通知、元數(shù)據(jù)傳遞的功能。

context 使用起來非常方便。源碼里對外提供了一個創(chuàng)建根節(jié)點 context 的函數(shù):

func Background() Context

background 是一個空的 context, 它不能被取消,沒有值,也沒有超時時間。 有了根節(jié)點 context,又提供了四個函數(shù)創(chuàng)建子節(jié)點 context:

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 會在函數(shù)傳遞間傳遞。只需要在適當(dāng)?shù)臅r間調(diào)用 cancel 函數(shù)向 goroutines 發(fā)出取消信號或者調(diào)用 Value 函數(shù)取出 context 中的值。

  • 不要將 Context 塞到結(jié)構(gòu)體里。直接將 Context 類型作為函數(shù)的第一參數(shù),而且一般都命名為ctx。
  • 不要向函數(shù)傳入一個 nil 的 context,如果你實在不知道傳什么,標準庫給你準備好了一個 context:todo。
  • 不要把本應(yīng)該作為函數(shù)參數(shù)的類型塞到 context 中,context 存儲的應(yīng)該是一些共同的數(shù)據(jù)。例如:登陸的 session、cookie 等。
  • 同一個 context 可能會被傳遞到多個 goroutine,別擔(dān)心,context 是并發(fā)安全的。

傳遞共享的數(shù)據(jù)

對于 Web 服務(wù)端開發(fā),往往希望將一個請求處理的整個過程串起來,這就非常依賴于 Thread Local(對于 Go 可理解為單個協(xié)程所獨有) 的變量,而在 Go 語言中并沒有這個概念,因此需要在函數(shù)調(diào)用的時候傳遞 context。

package main

import (
    "context"
    "fmt"
)
func main() {
    ctx := context.Background()
    process(ctx)
    ctx = context.WithValue(ctx, "traceId", "qcrao-2019")
    process(ctx)
}
func process(ctx context.Context) {
    traceId, ok := ctx.Value("traceId").(string)
    if ok {
        fmt.Printf("process over. trace_id=%s\n", traceId)
    } else {
        fmt.Printf("process over. no trace_id\n")
    }
}

運行結(jié)果:

process over. no trace_id
process over. trace_id=qcrao-2019

第一次調(diào)用 process 函數(shù)時,ctx 是一個空的 context,自然取不出來 traceId。第二次,通過 WithValue 函數(shù)創(chuàng)建了一個 context,并賦上了 traceId 這個 key,自然就能取出來傳入的 value 值。

取消 goroutine

我們先來設(shè)想一個場景:打開外賣的訂單頁,地圖上顯示外賣小哥的位置,而且是每秒更新 1 次。app 端向后臺發(fā)起 websocket 連接(現(xiàn)實中可能是輪詢)請求后,后臺啟動一個協(xié)程,每隔 1 秒計算 1 次小哥的位置,并發(fā)送給端。如果用戶退出此頁面,則后臺需要“取消”此過程,退出 goroutine,系統(tǒng)回收資源。

func Perform() {
    for {
        calculatePos()
        sendResult()
        time.Sleep(time.Second)
    }
}

如果需要實現(xiàn)“取消”功能,并且在不了解 context 功能的前提下,可能會這樣做:給函數(shù)增加一個指針型的 bool 變量,在 for 語句的開始處判斷 bool 變量是發(fā)由 true 變?yōu)?false,如果改變,則退出循環(huán)。

上面給出的簡單做法,可以實現(xiàn)想要的效果,沒有問題,但是并不優(yōu)雅,并且一旦協(xié)程數(shù)量多了之后,并且各種嵌套,就會很麻煩。優(yōu)雅的做法,自然就要用到 context。

func Perform(ctx context.Context) {
    for {
        calculatePos()
        sendResult()
        select {
        case <-ctx.Done():
            // 被取消,直接返回
            return
        case <-time.After(time.Second):
            // block 1 秒鐘 
        }
    }
}

主流程可能是這樣的:

ctx, cancel := context.WithTimeout(context.Background(), time.Hour)
go Perform(ctx)
// ……
// app 端返回頁面,調(diào)用cancel 函數(shù)
cancel()

注意一個細節(jié),WithTimeout 函數(shù)返回的 context 和 cancelFun 是分開的。context 本身并沒有取消函數(shù),這樣做的原因是取消函數(shù)只能由外層函數(shù)調(diào)用,防止子節(jié)點 context 調(diào)用取消函數(shù),從而嚴格控制信息的流向:由父節(jié)點 context 流向子節(jié)點 context。

防止 goroutine 泄漏

前面那個例子里,goroutine 還是會執(zhí)行完,最后返回,可能多浪費一些系統(tǒng)資源。這里改編一個 “如果不用 context 取消,goroutine 就會泄漏的例子”

func gen() <-chan int {
    ch := make(chan int)
    go func() {
        var n int
        for {
            ch <- n
            n++
            time.Sleep(time.Second)
        }
    }()
    return ch
}

這是一個可以生成無限整數(shù)的協(xié)程,但如果我只需要它產(chǎn)生的前 5 個數(shù),那么就會發(fā)生 goroutine 泄漏:

func main() {
    for n := range gen() {
        fmt.Println(n)
        if n == 5 {
            break
        }
    }
    // ……
}

當(dāng) n == 5 的時候,直接 break 掉。那么 gen 函數(shù)的協(xié)程就會執(zhí)行無限循環(huán),永遠不會停下來。發(fā)生了 goroutine 泄漏。

用 context 改進這個例子:

func gen(ctx context.Context) <-chan int {
    ch := make(chan int)
    go func() {
        var n int
        for {
            select {
            case <-ctx.Done():
                return
            case ch <- n:
                n++
                time.Sleep(time.Second)
            }
        }
    }()
    return ch
}
func main() {
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel() // 避免其他地方忘記 cancel,且重復(fù)調(diào)用不影響
    for n := range gen(ctx) {
        fmt.Println(n)
        if n == 5 {
            cancel()
            break
        }
    }
    // ……
}

增加一個 context,在 break 前調(diào)用 cancel 函數(shù),取消 goroutine。gen 函數(shù)在接收到取消信號后,直接退出,系統(tǒng)回收資源。

context.Value 的查找過程是怎樣的

和鏈表有點像,只是它的方向相反:Context 指向它的父節(jié)點,鏈表則指向下一個節(jié)點。通過 WithValue 函數(shù),可以創(chuàng)建層層的 valueCtx,存儲 goroutine 間可以共享的變量。

查找的時候,會向上查找到最后一個掛載的 context 節(jié)點,也就是離得比較近的一個父節(jié)點 context

到此這篇關(guān)于Go語言context上下文管理的使用的文章就介紹到這了,更多相關(guān)Go語言context上下文管理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go項目在linux服務(wù)器的部署詳細步驟

    Go項目在linux服務(wù)器的部署詳細步驟

    在今天的軟件開發(fā)中,使用Linux作為操作系統(tǒng)的比例越來越高,而Golang語言則因為其高效、簡潔和并發(fā)性能等特點,也被越來越多的開發(fā)者所青睞,這篇文章主要給大家介紹了關(guān)于Go項目在linux服務(wù)器的部署詳細步驟,需要的朋友可以參考下
    2023-09-09
  • golang 中signal包的Notify用法說明

    golang 中signal包的Notify用法說明

    這篇文章主要介紹了golang 中signal包的Notify用法說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-03-03
  • Go并發(fā)控制WaitGroup的使用場景分析

    Go并發(fā)控制WaitGroup的使用場景分析

    WaitGroup,可理解為Wait-Goroutine-Group,即等待一組goroutine結(jié)束,本文通過具體場景結(jié)合實際例子給大家介紹使用WaitGroup控制的實現(xiàn)方法,感興趣的朋友跟隨小編一起看看吧
    2021-07-07
  • Go設(shè)計模式之迭代器模式講解和代碼示例

    Go設(shè)計模式之迭代器模式講解和代碼示例

    迭代器是一種行為設(shè)計模式, 讓你能在不暴露復(fù)雜數(shù)據(jù)結(jié)構(gòu)內(nèi)部細節(jié)的情況下遍歷其中所有的元素,本文將為大家詳細介紹Go 迭代器模式,文中詳細的代碼示例,需要的朋友可以參考下
    2023-07-07
  • 淺談Golang如何使用Viper進行配置管理

    淺談Golang如何使用Viper進行配置管理

    在Golang生態(tài)中,Viper是一個不錯的開源配置管理框架,這篇文章主要為大家介紹了Golang如何使用Viper進行配置管理,需要的可以參考一下
    2023-06-06
  • Golang集成FFmpeg的音視頻處理的實現(xiàn)

    Golang集成FFmpeg的音視頻處理的實現(xiàn)

    FFmpeg是一個開源的音視頻處理工具,廣泛用于視頻轉(zhuǎn)換、截圖、處理和流媒體推送等操作,本文主要介紹了Golang集成FFmpeg的音視頻處理的實現(xiàn),具有一定的參考價值,感興趣的可以了解一下
    2025-02-02
  • GO語言實現(xiàn)列出目錄和遍歷目錄的方法

    GO語言實現(xiàn)列出目錄和遍歷目錄的方法

    這篇文章主要介紹了GO語言實現(xiàn)列出目錄和遍歷目錄的方法,涉及ioutil.ReadDir()與filepath.Walk()的應(yīng)用,是非常實用的技巧,需要的朋友可以參考下
    2014-12-12
  • GoLang基礎(chǔ)學(xué)習(xí)之go?test測試

    GoLang基礎(chǔ)學(xué)習(xí)之go?test測試

    相信每位編程開發(fā)者們應(yīng)該都知道,Golang作為一門標榜工程化的語言,提供了非常簡便、實用的編寫單元測試的能力,下面這篇文章主要給大家介紹了關(guān)于GoLang基礎(chǔ)學(xué)習(xí)之go?test測試的相關(guān)資料,需要的朋友可以參考下
    2022-08-08
  • Goland 關(guān)閉自動移除未使用的包操作

    Goland 關(guān)閉自動移除未使用的包操作

    這篇文章主要介紹了Goland 關(guān)閉自動移除未使用的包操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Golang常量iota的使用實例

    Golang常量iota的使用實例

    今天小編就為大家分享一篇關(guān)于Golang常量iota的使用實例,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-01-01

最新評論

镇沅| 河西区| 蓬安县| 电白县| 南充市| 遵化市| 卢湾区| 新乐市| 玉田县| 廉江市| 内江市| 安远县| 图们市| 望都县| 镇宁| 定边县| 公安县| 奉新县| 塔城市| 隆化县| 四川省| 民丰县| 泗水县| 福安市| 洪雅县| 衡山县| 邹城市| 佛教| 夏邑县| 白朗县| 永新县| 和龙市| 双流县| 垣曲县| 新昌县| 浦城县| 张掖市| 东乡族自治县| 临江市| 通化县| 黎城县|