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

Go語(yǔ)言Goroutines?泄漏場(chǎng)景與防治解決分析

 更新時(shí)間:2022年12月15日 09:44:38   作者:小馬別過(guò)河  
這篇文章主要為大家介紹了Go語(yǔ)言Goroutines?泄漏場(chǎng)景與防治解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

場(chǎng)景

Go 有很多自動(dòng)管理內(nèi)存的功能。比如:

  • 變量分配到堆內(nèi)存還是棧內(nèi)存,編譯器會(huì)通過(guò)逃逸分析(escpage analysis)來(lái)判斷;
  • 堆內(nèi)存的垃圾自動(dòng)回收。

即便如此,如果編碼不謹(jǐn)慎,我們還是有可能導(dǎo)致內(nèi)存泄漏的,最常見(jiàn)的是 goroutine 泄漏,比如下面的函數(shù):

func goroutinueLeak() {
	ch := make(chan int)
	go func(ch chan int) {
    // 因?yàn)?ch 一直沒(méi)有數(shù)據(jù),所以這個(gè)協(xié)程會(huì)阻塞在這里。
		val := <-ch
		fmt.Println(val)
	}(ch)
}

由于ch一直沒(méi)有發(fā)送數(shù)據(jù),所以我們開(kāi)啟的 goroutine 會(huì)一直阻塞。每次調(diào)用goroutinueLeak都會(huì)泄漏一個(gè)goroutine,從監(jiān)控面板看到話,goroutinue 數(shù)量會(huì)逐步上升,直至服務(wù) OOM。

Goroutine 泄漏常見(jiàn)原因

channel 發(fā)送端導(dǎo)致阻塞

使用 context 設(shè)置超時(shí)是常見(jiàn)的一個(gè)場(chǎng)景,試想一下,下面的函數(shù)什么情況下會(huì) goroutine 泄漏 ?

func contextLeak() error {
	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
	defer cancel()
	ch := make(chan int)
        //g1
	go func() {
		// 獲取數(shù)據(jù),比如網(wǎng)絡(luò)請(qǐng)求,可能時(shí)間很久
		val := RetriveData()
		ch <- val
	}()
	select {
	case <-ctx.Done():
		return errors.New("timeout")
	case val := <-ch:
		fmt.Println(val)
	}
	return nil
}

RetriveData() 如果超時(shí)了,那么contextLeak() 會(huì)返回 error,本函數(shù)執(zhí)行結(jié)束。而我們開(kāi)啟的協(xié)程g1,由于沒(méi)有接受者,會(huì)阻塞在 ch<-val。

解決方法也能簡(jiǎn)單,比如可以給ch加上緩存。

channel 接收端導(dǎo)致阻塞

開(kāi)篇給出的函數(shù)goroutinueLeak,就是因?yàn)閏hannel的接收端收不到數(shù)據(jù),導(dǎo)致阻塞。

這里舉出另一個(gè)例子,下面的函數(shù),是否有可能 goroutinue 泄漏?

func errorAssertionLeak() {
	ch := make(chan int)
        // g1
	go func() {
		val := <-ch
		fmt.Println(val)
	}()
        // RetriveSomeData 表示獲取數(shù)據(jù),比如從網(wǎng)絡(luò)上
	val, err := RetriveSomeData()
	if err != nil {
		return
	}
	ch <- val
	return nil
}

如果 RetriveSomeData() 返回的err 不為 nil,那么本函數(shù)中斷,也就不會(huì)有數(shù)據(jù)發(fā)送給ch,這導(dǎo)致協(xié)程g1會(huì)一直阻塞。

如何預(yù)防

goroutine 泄漏往往需要服務(wù)運(yùn)行一段時(shí)間后,才會(huì)被發(fā)覺(jué)。

我們可以通過(guò)監(jiān)控 goroutine 數(shù)量來(lái)判斷是否有 goroutine 泄漏;或者用 pprof(之前文章介紹過(guò)的) 來(lái)定位泄漏的 goroutine。但這些已經(jīng)是亡羊補(bǔ)牢了。最理想的情況是,我們?cè)陂_(kāi)發(fā)的過(guò)程中,就能發(fā)現(xiàn)。

本文推薦的做法是,使用單元測(cè)試。以開(kāi)篇的 goroutinueLeak 為例子,我們寫(xiě)個(gè)單測(cè):

func TestLeak(t *testing.T) {
	goroutinueLeak()
}

執(zhí)行 go test,發(fā)現(xiàn)測(cè)試是通過(guò)的:

=== RUN   TestLeak
--- PASS: TestLeak (0.00s)
PASS
ok      example/leak    0.598s

這是是因?yàn)閱螠y(cè)默認(rèn)不會(huì)檢測(cè) goroutine 泄漏的。

我們可以在單測(cè)中,加入U(xiǎn)ber 團(tuán)隊(duì)提供的 uber-go/goleak 包:

import (
	"testing"
	"go.uber.org/goleak"
)
func TestLeak(t *testing.T) {
        // 加上這行代碼,就會(huì)自動(dòng)檢測(cè)是否 goroutine 泄漏
	defer goleak.VerifyNone(t)
	goroutinueLeak()
}

這時(shí)候執(zhí)行 go test,輸出:

=== RUN   TestLeak
    /xxx/leak_test.go:12: found unexpected goroutines:
        [Goroutine 21 in state chan receive, with example/leak.goroutinueLeak.func1 on top of the stack:
        goroutine 21 [chan receive]:
        example/leak.goroutinueLeak.func1(0x0)
            /xxx/leak.go:9 +0x27
        created by example/leak.goroutinueLeak
            /xxx/leak.go:8 +0x7a
        ]
--- FAIL: TestLeak (0.46s)
FAIL
FAIL    example/leak    0.784s

這時(shí)候單測(cè)會(huì)因?yàn)?goroutine 泄漏而不通過(guò)。

如果你覺(jué)得每個(gè)測(cè)試用例都要加上 defer goleak.VerifyNone(t) 太繁瑣的話(特別是在已有的項(xiàng)目中加上),goleak 提供了在 TestMain 中使用的方法VerifyTestMain,上面的單測(cè)可以修改成:

func TestLeak(t *testing.T) {
	goroutinueLeak()
}
func TestMain(m *testing.M) {
	goleak.VerifyTestMain(m)
}

總結(jié)

雖然我的文章經(jīng)常提及單測(cè),但我本人不是單元測(cè)試的忠實(shí)粉絲。扎實(shí)的基礎(chǔ),充分的測(cè)試,負(fù)責(zé)任的態(tài)度也是非常重要的。

引用

以上就是Go語(yǔ)言Goroutines 泄漏場(chǎng)景與防治解決分析的詳細(xì)內(nèi)容,更多關(guān)于Go Goroutines 泄漏防治的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go語(yǔ)言字符串處理庫(kù)strings包詳解

    Go語(yǔ)言字符串處理庫(kù)strings包詳解

    本文詳細(xì)介紹了Go語(yǔ)言中的strings庫(kù)的使用方法,包括字符串的查找、替換、分割、比較、大小寫(xiě)轉(zhuǎn)換等操作,strings庫(kù)是Go語(yǔ)言中非常重要且功能豐富的標(biāo)準(zhǔn)庫(kù),幾乎涵蓋了所有字符串處理的需求
    2024-09-09
  • go程序執(zhí)行交叉編譯的流程步驟

    go程序執(zhí)行交叉編譯的流程步驟

    go程序可用通過(guò)交叉編譯的方式在一個(gè)平臺(tái)輸出多個(gè)平臺(tái)可運(yùn)行的二進(jìn)制包,本文給大家詳細(xì)介紹了go程序執(zhí)行交叉編譯的流程步驟,文中有詳細(xì)的代碼示例供大家參考,需要的朋友可以參考下
    2024-07-07
  • golang動(dòng)態(tài)創(chuàng)建類(lèi)的示例代碼

    golang動(dòng)態(tài)創(chuàng)建類(lèi)的示例代碼

    這篇文章主要介紹了golang動(dòng)態(tài)創(chuàng)建類(lèi)的實(shí)例代碼,本文通過(guò)實(shí)例代碼給大家講解的非常詳細(xì),需要的朋友可以參考下
    2023-06-06
  • Go語(yǔ)言實(shí)現(xiàn)文件上傳

    Go語(yǔ)言實(shí)現(xiàn)文件上傳

    這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言實(shí)現(xiàn)文件上傳,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • golang中package?is?not?in?GOROOT報(bào)錯(cuò)的真正解決辦法

    golang中package?is?not?in?GOROOT報(bào)錯(cuò)的真正解決辦法

    這篇文章主要給大家介紹了關(guān)于golang中package?is?not?in?GOROOT報(bào)錯(cuò)的真正解決辦法,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)同樣遇到這個(gè)問(wèn)題的朋友具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2023-03-03
  • Go語(yǔ)言共享內(nèi)存讀寫(xiě)實(shí)例分析

    Go語(yǔ)言共享內(nèi)存讀寫(xiě)實(shí)例分析

    這篇文章主要介紹了Go語(yǔ)言共享內(nèi)存讀寫(xiě)方法,實(shí)例分析了共享內(nèi)存的原理與讀寫(xiě)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • 淺談Go中數(shù)字轉(zhuǎn)換字符串的正確姿勢(shì)

    淺談Go中數(shù)字轉(zhuǎn)換字符串的正確姿勢(shì)

    這篇文章主要介紹了淺談Go中數(shù)字轉(zhuǎn)換字符串的正確姿勢(shì),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • 詳解Golang中g(shù)cache模塊的基本使用

    詳解Golang中g(shù)cache模塊的基本使用

    這篇文章主要通過(guò)結(jié)合商業(yè)項(xiàng)目的使用場(chǎng)景,為大家介紹了gcache的基本使用、緩存控制以及淘汰策略。使用gcache做緩存處理,簡(jiǎn)單方便易上手
    2022-11-11
  • Go語(yǔ)言接口與多態(tài)詳細(xì)介紹

    Go語(yǔ)言接口與多態(tài)詳細(xì)介紹

    Go語(yǔ)言的接口類(lèi)型是一組方法定義的集合,它體現(xiàn)了多態(tài)性、高內(nèi)聚和低耦合的設(shè)計(jì)思想,接口通過(guò)interface關(guān)鍵字定義,無(wú)需實(shí)現(xiàn)具體方法,任何實(shí)現(xiàn)了接口所有方法的類(lèi)型即視為實(shí)現(xiàn)了該接口,感興趣的朋友一起看看吧
    2024-09-09
  • Go語(yǔ)言中實(shí)現(xiàn)完美錯(cuò)誤處理實(shí)踐分享

    Go語(yǔ)言中實(shí)現(xiàn)完美錯(cuò)誤處理實(shí)踐分享

    Go?語(yǔ)言是一門(mén)非常流行的編程語(yǔ)言,由于其高效的并發(fā)編程和出色的網(wǎng)絡(luò)編程能力,越來(lái)越受到廣大開(kāi)發(fā)者的青睞。本文我們就來(lái)深入探討一下Go?語(yǔ)言中的錯(cuò)誤處理機(jī)制吧
    2023-04-04

最新評(píng)論