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

Golang中errgroup的常見(jiàn)誤用詳解

 更新時(shí)間:2024年01月29日 11:16:44   作者:apocelipes  
errgroup和sync.WaitGroup類似,都可以發(fā)起執(zhí)行并等待一組協(xié)程直到所有協(xié)程運(yùn)行結(jié)束,本文主要為大家整理了一些errgroup的常見(jiàn)誤用,有需要的可以參考下

errgroup想必稍有經(jīng)驗(yàn)的golang程序員都應(yīng)該聽(tīng)說(shuō)過(guò),實(shí)際項(xiàng)目中用過(guò)的也應(yīng)該不在少數(shù)。它和sync.WaitGroup類似,都可以發(fā)起執(zhí)行并等待一組協(xié)程直到所有協(xié)程運(yùn)行結(jié)束。除此之外errgroup還可以在協(xié)程出錯(cuò)時(shí)取消當(dāng)前的context,以及它還能控制可運(yùn)行的協(xié)程的數(shù)量。

但在日常的代碼review時(shí)我注意到了幾個(gè)比較常見(jiàn)的問(wèn)題,這些問(wèn)題有的無(wú)傷大雅最多只會(huì)造成一些性能損失,有的則會(huì)導(dǎo)致資源泄露甚至是死鎖崩潰。

這里對(duì)這些比較典型的誤用做下記錄。

多余的context嵌套

先說(shuō)個(gè)不是很常見(jiàn)但我還是遇到過(guò)兩三次的不太妥當(dāng)?shù)挠梅ā?/p>

我們知道errgroup在協(xié)程返回錯(cuò)誤的時(shí)候會(huì)取消掉創(chuàng)建時(shí)傳入的context,這是為了能讓同組的其他協(xié)程知道有錯(cuò)誤發(fā)生應(yīng)該盡快退出執(zhí)行。

所以errgroup使用的context應(yīng)該是派生于當(dāng)前上下文的新的context,這樣才不會(huì)讓可能的取消操作影響到errgroup之外的范圍。

因此第一個(gè)常見(jiàn)誤用出現(xiàn)了:

func DoWork(ctx context.Context) {
    errCtx, cancel := context.WithCancel(ctx)
    defer cancel()
    group, errCtx := errgroup.WithContext(ctx)
    ...
}

誤用在哪呢?答案是context會(huì)自動(dòng)幫我們派生出新的context,除了需要設(shè)置超時(shí)一般不需要再次額外封裝,看源代碼:

// https://github.com/golang/sync/blob/master/errgroup/errgroup.go
 
// WithContext returns a new Group and an associated Context derived from ctx.
//
// The derived Context is canceled the first time a function passed to Go
// returns a non-nil error or the first time Wait returns, whichever occurs
// first.
func WithContext(ctx context.Context) (*Group, context.Context) {
	ctx, cancel := withCancelCause(ctx)
	return &Group{cancel: cancel}, ctx
}
 
// https://github.com/golang/sync/blob/master/errgroup/go120.go
 
func withCancelCause(parent context.Context) (context.Context, func(error)) {
	return context.WithCancelCause(parent)
}

多于的嵌套會(huì)浪費(fèi)內(nèi)存,以及會(huì)對(duì)性能帶來(lái)負(fù)面影響,尤其是需要從context里取出某些value的時(shí)候,因?yàn)槿alue是對(duì)一層層嵌套的context遞歸查找的,嵌套層數(shù)越多查找就有可能越慢。

不過(guò)前面也說(shuō)到了,有一種情況是允許的,那就是對(duì)整個(gè)errgroup所有的協(xié)程設(shè)置超時(shí):

func DoWork(ctx context.Context) {
    errCtx, cancel := context.WithTimeout(ctx, 10 * time.Second)
    defer cancel()
    group, errCtx := errgroup.WithContext(ctx)
    ...
}

目前想設(shè)置超時(shí)只能這樣做,所以這種算是特例。

Wait返回的時(shí)機(jī)

第二種誤用比第一種要常見(jiàn)些。主要是對(duì)errgroup的行為理解上有誤解。

這種誤解經(jīng)常表現(xiàn)為:如果協(xié)程返回錯(cuò)誤或者ctx的超時(shí)被觸發(fā),Wait方法就會(huì)立即返回。

這并不是事實(shí)。

先來(lái)看看Wait的文檔怎么說(shuō)的:

Wait blocks until all function calls from the Go method have returned, then returns the first non-nil error (if any) from them.

Wait需要等到所有g(shù)oroutine返回后它才會(huì)返回。哪怕超時(shí)了,context取消了也一樣,需要先等所有協(xié)程退出。再來(lái)看代碼:

// https://github.com/golang/sync/blob/master/errgroup/errgroup.go
 
func (g *Group) Wait() error {
	g.wg.Wait()
	if g.cancel != nil {
		g.cancel(g.err)
	}
	return g.err
}

可以看到確實(shí)需要先等所有協(xié)程返回。如果你觀察比較敏銳的話,其實(shí)能發(fā)現(xiàn)errgroup會(huì)對(duì)協(xié)程做包裝,會(huì)不會(huì)包裝的代碼里有什么辦法提前中止協(xié)程的執(zhí)行呢?還是來(lái)看代碼:

// https://github.com/golang/sync/blob/master/errgroup/errgroup.go
 
func (g *Group) Go(f func() error) {
	// 檢查當(dāng)前協(xié)程是否可運(yùn)行的代碼,先忽略
 
	g.wg.Add(1)
	go func() {
		defer g.done()  // 重點(diǎn)在這
 
		if err := f(); err != nil {
			g.errOnce.Do(func() {
				g.err = err
				if g.cancel != nil {
					g.cancel(g.err)
				}
			})
		}
	}()
}

注意那個(gè)defer,這意味著done只有在包裝的函數(shù)運(yùn)行結(jié)束(在你自己的函數(shù)f運(yùn)行完并設(shè)置了error以及取消了ctx之后)時(shí)才會(huì)執(zhí)行。

如果你自己的函數(shù)里不檢查超時(shí)和上下文是否被取消,那leak和卡死問(wèn)題就要找上門來(lái)了,比如下面這樣的:

func main() {
    errCtx, cancel := context.WithTimeout(context.Background(), 1 * time.Second)
    defer cancel()
    group, errCtx := errgroup.WithContext(errCtx)
    group.Go(func () error {
        time.Sleep(10 * time.Second)
        fmt.Println("running")
        return nil
    })
    group.Go(func () error {
        return errors.New("error")
    })
    fmt.Println(group.Wait())
}

猜猜運(yùn)行結(jié)果和執(zhí)行時(shí)間。答案是running\nerror\n,運(yùn)行需要10秒以上。

這種誤用也很好識(shí)別,只要傳給Go方法的函數(shù)里沒(méi)有好好處理errCtx,那多半是有問(wèn)題的。

不過(guò)要說(shuō)句公道話,Go的參數(shù)形式不符合一般使用context的慣例,Wait的行為和其他能自主取消線程執(zhí)行的語(yǔ)言也不一樣造成了誤用,語(yǔ)言和接口設(shè)計(jì)得背一半鍋不能全賴用它的程序員。

SetLimit和死鎖

這種就更常見(jiàn)了,尤其發(fā)生在把errgroup當(dāng)成普通協(xié)程池用的時(shí)候。

先來(lái)我最愛(ài)的猜謎游戲,下面的代碼運(yùn)行結(jié)果是什么?

func main() {
    group, _ := errgroup.WithContext(context.Background())
    group.SetLimit(2) // 想法:只允許2個(gè)協(xié)程同時(shí)運(yùn)行,但多個(gè)任務(wù)提交到“協(xié)程池”
    group.Go(func () error {
        fmt.Println("running 1")
        // 運(yùn)行子任務(wù)
        group.Go(func () error {
            fmt.Println("sub running 1")
            return nil
        })
        group.Go(func () error {
            fmt.Println("sub running 2")
            return nil
        })
        return nil
    })
    group.Go(func () error {
        fmt.Println("running 2")
        // 運(yùn)行子任務(wù)
        group.Go(func () error {
            fmt.Println("sub running 3")
            return nil
        })
        group.Go(func () error {
            fmt.Println("sub running 4")
            return nil
        })
        return nil
    })
    fmt.Println(group.Wait())
}

答案是會(huì)死鎖panic。而且是100%觸發(fā)。

我會(huì)詳細(xì)的解釋這是為什么,但在之前我要說(shuō)一個(gè)重要的知識(shí)點(diǎn):

SetLimit設(shè)置的不是同時(shí)在運(yùn)行的協(xié)程數(shù)量,而是設(shè)置errgroup內(nèi)最多同時(shí)能持有多少個(gè)協(xié)程,errgroup持有的協(xié)程可以在運(yùn)行也可以在等待運(yùn)行。

如果每個(gè)running的sub running只有一個(gè),那么有小概率不會(huì)死鎖,所以我特地每組創(chuàng)建了兩個(gè),原因沒(méi)那么復(fù)雜,看來(lái)后面的解釋之后可以自行推理。

下面來(lái)解釋,首先看SetLimit的代碼,一切是從這開(kāi)始的:

// https://github.com/golang/sync/blob/master/errgroup/errgroup.go
 
// SetLimit limits the number of active goroutines in this group to at most n.
// A negative value indicates no limit.
//
// Any subsequent call to the Go method will block until it can add an active
// goroutine without exceeding the configured limit.
//
// The limit must not be modified while any goroutines in the group are active.
func (g *Group) SetLimit(n int) {
	if n < 0 {
		g.sem = nil
		return
	}
	if len(g.sem) != 0 {
		panic(fmt.Errorf("errgroup: modify limit while %v goroutines in the group are still active", len(g.sem)))
	}
	g.sem = make(chan token, n)
}

g.semchan strcut{}。做的事很簡(jiǎn)單,如果參數(shù)大于0就按參數(shù)初始化一個(gè)長(zhǎng)度為n的chan給g.sem,小于0就清空g.sem。如果你經(jīng)驗(yàn)比較豐富的話,已經(jīng)可以看出來(lái)這是一個(gè)簡(jiǎn)單的ticket pool模式了,這個(gè)模式在grpc里也有應(yīng)用。

ticket pool模式的原理是設(shè)置一個(gè)固定大小為n的空chan,然后協(xié)程要運(yùn)行的時(shí)候向這個(gè)chan寫入數(shù)據(jù),協(xié)程運(yùn)行結(jié)束的時(shí)候從chan里把寫入的數(shù)據(jù)讀出(可能會(huì)讀到別人寫進(jìn)去的,但只要遵循這個(gè)寫入讀出的順序就沒(méi)問(wèn)題)。如果chan的寫入阻塞了,就說(shuō)明已經(jīng)有n個(gè)協(xié)程在運(yùn)行了,新的協(xié)程需要等到有協(xié)程執(zhí)行完并讀出數(shù)據(jù)后才能繼續(xù)執(zhí)行;正常情況下讀出操作不會(huì)被阻塞。這個(gè)是限制goroutine數(shù)量的最常見(jiàn)的手段之一。根據(jù)寫入操作實(shí)在協(xié)程內(nèi)部還是發(fā)起協(xié)程的調(diào)用者那里進(jìn)行,這個(gè)模式還能分別控制“最大同時(shí)運(yùn)行的goroutine數(shù)量”或“goroutine總數(shù)量”。其中goroutine的總數(shù)量 = 在運(yùn)行的goroutine數(shù)量 + 其他等待運(yùn)行g(shù)oroutine的數(shù)量。

而errgroup屬于后者。還記得Go的代碼里我注釋掉的那部分吧,現(xiàn)在可以看了:

// https://github.com/golang/sync/blob/master/errgroup/errgroup.go
 
func (g *Group) Go(f func() error) {
	if g.sem != nil {
		g.sem <- token{} // token是struct{}
	}
 
	g.wg.Add(1)
	go func() {
		defer g.done()
 
		if err := f(); err != nil {
			// 設(shè)置錯(cuò)誤值
		}
	}()
}
 
func (g *Group) done() {
	if g.sem != nil {
		<-g.sem // 從ticket pool里讀出
	}
	g.wg.Done()
}

進(jìn)入Go的時(shí)候并沒(méi)有啟動(dòng)協(xié)程,而是先檢查sem,如果有設(shè)置limit,就需要按操作ticket pool的流程先寫入數(shù)據(jù)。寫入成功才會(huì)創(chuàng)建協(xié)程,協(xié)程運(yùn)行結(jié)束后把數(shù)據(jù)讀出。這樣限制了errgroup最大可以持有的協(xié)程數(shù)量,因?yàn)槌^(guò)數(shù)量限制會(huì)阻塞住不創(chuàng)建新的協(xié)程。

Go完成sem的寫入并執(zhí)行g(shù)o語(yǔ)句之前,errgroup并沒(méi)有“持有”go語(yǔ)句創(chuàng)建的這個(gè)協(xié)程。協(xié)程運(yùn)行結(jié)束并把sem的數(shù)據(jù)讀出后,group將不會(huì)繼續(xù)“持有”這個(gè)協(xié)程。

問(wèn)題就出在寫入那里。假設(shè)調(diào)度器是這樣運(yùn)行我們的猜謎代碼的:

  • 先啟動(dòng)running 1的協(xié)程,sem空位有2個(gè),正常運(yùn)行,running 1運(yùn)行結(jié)束后它寫入的數(shù)據(jù)才會(huì)被讀出
  • 接著啟動(dòng)running 2,sem還剩一個(gè)空位,沒(méi)問(wèn)題,running 2運(yùn)行結(jié)束后它寫入的數(shù)據(jù)才會(huì)被讀出
  • running 2先被執(zhí)行,于是準(zhǔn)備創(chuàng)建sub running 3的協(xié)程
  • 這時(shí)sem沒(méi)空位了,創(chuàng)建sub running 3的Go阻塞
  • 調(diào)度器發(fā)現(xiàn)running 2被阻塞了,于是讓running 1執(zhí)行(假設(shè)而已,多核處理器上很可能是同時(shí)運(yùn)行的)
  • running 1輸出后準(zhǔn)備創(chuàng)建sub running 1的協(xié)程
  • sem還是滿的,Go又阻塞了
  • 調(diào)度器發(fā)現(xiàn)running 1和running 2都阻塞了,于是只能讓main goroutine執(zhí)行(這里忽略runtime自己的協(xié)程,因?yàn)椴挥绊懰梨i檢測(cè)結(jié)果)
  • main阻塞在Wait上,所有其他協(xié)程執(zhí)行完才能繼續(xù)執(zhí)行
  • 沒(méi)有能繼續(xù)運(yùn)行下去的協(xié)程,全都阻塞了(注意是阻塞不是sleep),死鎖檢測(cè)發(fā)現(xiàn)這種情況,panic

我知道實(shí)際執(zhí)行順序肯定不一樣,但死鎖的原因一樣的:因?yàn)橹暗膮f(xié)程沒(méi)有讓出ticket pool,后面的子任務(wù)需要向pool寫入,而前面占有pool的協(xié)程需要等子任務(wù)執(zhí)行完才會(huì)讓出pool。這是一個(gè)典型的循環(huán)依賴導(dǎo)致的死鎖,誘因是同一個(gè)errgroup的嵌套使用。

是什么導(dǎo)致了你踩坑呢?最大的可能是文檔里那個(gè)“active”。這個(gè)詞太模糊了,你可以發(fā)現(xiàn)它即能代指running又能代指runnable,還能兩個(gè)同時(shí)代指。這里因?yàn)橄旅孢€有一段話,所以可以根據(jù)上下文估摸著猜出active想代指的是所有被創(chuàng)建出來(lái)的協(xié)程不管它們?cè)诓辉谶\(yùn)行。但如果你只看了第一段話就先入為主放心大膽用的話,坑就來(lái)了。這樣的詞缺少足夠的上下文時(shí)連母語(yǔ)者都會(huì)覺(jué)得有二義性,更何況我們這些作為第二語(yǔ)言甚至第三語(yǔ)言的人。

而errgroup選擇限制goroutine總數(shù)量也是有原因的:只限制同時(shí)運(yùn)行的goroutine的數(shù)量就沒(méi)法限制協(xié)程的總數(shù)量,協(xié)程雖然很輕量,但還是要占用內(nèi)存以及花費(fèi)cpu資源來(lái)調(diào)度的,不受控制很可能會(huì)產(chǎn)生災(zāi)難性后果,比如一個(gè)不當(dāng)心在循環(huán)里創(chuàng)建了數(shù)百萬(wàn)個(gè)協(xié)程導(dǎo)致嚴(yán)重的內(nèi)存占用和調(diào)度壓力,控制了總數(shù)量這類問(wèn)題就可以避免。

幸運(yùn)的是,這個(gè)誤用也很好識(shí)別,但凡有嵌套使用同一個(gè)errgroup的時(shí)候,就要警報(bào)大作了。

更幸運(yùn)的是,如果你沒(méi)有嵌套調(diào)用,那么這個(gè)SetLimit不管設(shè)置成哪個(gè)數(shù)字,都能正常限制頂層的goroutine的數(shù)量(或者不做限制),它不能限制的是從頂層協(xié)程里嵌套調(diào)用派生出的子協(xié)程,只要不嵌套調(diào)用同一個(gè)group,什么問(wèn)題的不會(huì)有。

前面兩種誤用都是該避免的,然而嵌套的errgroup雖然不多見(jiàn)但確實(shí)有用處,所以我也會(huì)提供寫簡(jiǎn)單的解決方案以供參考。

第一種是設(shè)置一個(gè)足夠的limit數(shù)值,聰明人應(yīng)該發(fā)現(xiàn)了,如果把limit設(shè)置成希望group里同時(shí)存在的協(xié)程的總數(shù)量(頂層+所有嵌套派生的),問(wèn)題就能避免。這沒(méi)錯(cuò),但我不推薦,兩點(diǎn)原因:

  • 設(shè)置成總數(shù)后起不到限制同時(shí)運(yùn)行的協(xié)程的數(shù)量,在go里控制同時(shí)運(yùn)行的協(xié)程數(shù)量是個(gè)很麻煩的事,limit通常只能起到“上限”的作用,但如果上限設(shè)置大了就容易出現(xiàn)問(wèn)題。比如你的系統(tǒng)只能同時(shí)運(yùn)行3個(gè)協(xié)程,你還有別的任務(wù)占用了一個(gè)協(xié)程在運(yùn)行,為了避免死鎖你設(shè)置了limit為4,這時(shí)候資源搶占和協(xié)程調(diào)度延遲都會(huì)明顯上升,出現(xiàn)這類情況你的系統(tǒng)就離崩潰只有一步之遙了。
  • 算這個(gè)數(shù)量很麻煩,上面的例子你可以很簡(jiǎn)單算出是4,如果我再套一層或者加上幾個(gè)可以跳過(guò)Go調(diào)用的條件分支呢?而且limit設(shè)置多了是起不到限制goroutine數(shù)量的作用的,設(shè)少了會(huì)死鎖。
  • limit多半是個(gè)寫死的常量或者干脆是魔數(shù),那么下次協(xié)程的邏輯改了這個(gè)數(shù)字多半得跟著改,如果你算錯(cuò)了或者忘記改了,那么你就慘了,死鎖就像個(gè)地雷一樣埋下了。

綜上,你應(yīng)該用第二種方法:永遠(yuǎn)不要嵌套使用同一個(gè)errgroup,真有嵌套需求也應(yīng)該使用新的errgroup實(shí)例,這樣可以避免死鎖,也最符合當(dāng)前需求的語(yǔ)義:

func main() {
    group, errCtx := errgroup.WithContext(context.Background())
    group.SetLimit(1) // 想法:只允許2個(gè)協(xié)程同時(shí)運(yùn)行,但多個(gè)任務(wù)提交到“協(xié)程池”
    group.Go(func () error {
        fmt.Println("running 1")
        // 運(yùn)行子任務(wù)
        // 新建一個(gè)errgroup,上下文使用外層group的
        subGroup, _ := errgroup.WithContext(errCtx)
        subGroup.SetLimit(1)
        subGroup.Go(func () error {
            fmt.Println("sub running 1")
            return nil
        })
        subGroup.Go(func () error {
            fmt.Println("sub running 2")
            return nil
        })
        fmt.Println(subGroup.Wait())
        return nil
    })
    group.Go(func () error {
        fmt.Println("running 2")
        // 運(yùn)行子任務(wù)
        subGroup, _ := errgroup.WithContext(errCtx)
        subGroup.SetLimit(1)
        subGroup.Go(func () error {
            fmt.Println("sub running 3")
            return nil
        })
        subGroup.Go(func () error {
            fmt.Println("sub running 4")
            return nil
        })
        fmt.Println(subGroup.Wait())
        return nil
    })
    fmt.Println(group.Wait())
}

是的,現(xiàn)在所有l(wèi)imit設(shè)置成1也不會(huì)死鎖。因?yàn)闆](méi)有嵌套調(diào)用,因此也沒(méi)有資源間的循環(huán)依賴了。

當(dāng)然還有終極方案:別把errgroup當(dāng)成協(xié)程池,如果你有復(fù)雜功能依賴于協(xié)程池找個(gè)功能全面的真正的協(xié)程池比如ants之類的用。

對(duì)了。你問(wèn)SetLimit傳0進(jìn)去會(huì)發(fā)生什么,那當(dāng)然是直接死鎖了。這也符合語(yǔ)義,因?yàn)槟愕膅roup里不能有任何協(xié)程,這時(shí)候再調(diào)Go當(dāng)然是不對(duì)的,死鎖panic也是應(yīng)該的。所以傳0進(jìn)去導(dǎo)致死鎖這不算坑,也算不上誤用。

總結(jié)

總結(jié)下上面三個(gè)誤用:

  • 傳遞有多余嵌套的context給errgroup
  • 在加入errgroup的協(xié)程里沒(méi)有正確處理context取消和超時(shí)
  • 嵌套使用同一個(gè)errgroup

已有的靜態(tài)分析工具不是很能識(shí)別這類問(wèn)題,要么自己寫個(gè)能識(shí)別的,要么只能靠review把關(guān)了。

比較大眾的觀點(diǎn)認(rèn)為go簡(jiǎn)單易用,但實(shí)際上并不總是如此,有句話叫“Simple is not Easy”,go的使用者需要時(shí)刻為“大道至簡(jiǎn)”付出相應(yīng)的代價(jià)。

以上就是Golang中errgroup的常見(jiàn)誤用詳解的詳細(xì)內(nèi)容,更多關(guān)于Go errgroup的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Golang通過(guò)SSH執(zhí)行交換機(jī)操作實(shí)現(xiàn)

    Golang通過(guò)SSH執(zhí)行交換機(jī)操作實(shí)現(xiàn)

    這篇文章主要介紹了Golang通過(guò)SSH執(zhí)行交換機(jī)操作實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • Go語(yǔ)言中你所不知道的位操作用法

    Go語(yǔ)言中你所不知道的位操作用法

    位運(yùn)算可能在平常的編程中使用的并不多,但涉及到底層優(yōu)化,一些算法及源碼可能會(huì)經(jīng)常遇見(jiàn)。下面這篇文章主要給大家介紹了關(guān)于Go語(yǔ)言中你所不知道的位操作用法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下。
    2017-12-12
  • golang time包的用法詳解

    golang time包的用法詳解

    這篇文章主要介紹了golang time包的用法詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • Golang拾遺之自定義類型和方法集詳解

    Golang拾遺之自定義類型和方法集詳解

    golang拾遺主要是用來(lái)記錄一些遺忘了的、平時(shí)從沒(méi)注意過(guò)的golang相關(guān)知識(shí)。這篇文章主要整理了一下Golang如何自定義類型和方法集,需要的可以參考一下
    2023-02-02
  • Go語(yǔ)言標(biāo)準(zhǔn)輸入輸出庫(kù)的基本使用教程

    Go語(yǔ)言標(biāo)準(zhǔn)輸入輸出庫(kù)的基本使用教程

    輸入輸出在任何一門語(yǔ)言中都必須提供的一個(gè)功能,下面這篇文章主要給大家介紹了關(guān)于Go語(yǔ)言標(biāo)準(zhǔn)輸入輸出庫(kù)的基本使用,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-02-02
  • Go中的Timer 和 Ticker詳解

    Go中的Timer 和 Ticker詳解

    在日常開(kāi)發(fā)中,我們可能會(huì)遇到需要延遲執(zhí)行或周期性地執(zhí)行一些任務(wù),這個(gè)時(shí)候就需要用到 Go 語(yǔ)言中的定時(shí)器,本文將會(huì)對(duì)這兩種定時(shí)器類型進(jìn)行介紹,感興趣的朋友一起看看吧
    2024-07-07
  • Golang使用pprof檢查內(nèi)存泄漏的全過(guò)程

    Golang使用pprof檢查內(nèi)存泄漏的全過(guò)程

    pprof 是golang提供的一款分析工具,可以分析CPU,內(nèi)存的使用情況,本篇文章關(guān)注它在分析內(nèi)存泄漏方面的應(yīng)用,本文給大家介紹了Golang使用pprof檢查內(nèi)存泄漏的全過(guò)程,文中通過(guò)代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2024-02-02
  • 手把手帶你走進(jìn)Go語(yǔ)言之常量解析

    手把手帶你走進(jìn)Go語(yǔ)言之常量解析

    這篇文章主要介紹了Go語(yǔ)言之常量解析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-09-09
  • Go語(yǔ)言中更優(yōu)雅的錯(cuò)誤處理

    Go語(yǔ)言中更優(yōu)雅的錯(cuò)誤處理

    Go語(yǔ)言中的錯(cuò)誤處理是一個(gè)被大家經(jīng)常拿出來(lái)討論的話題(另外一個(gè)是泛型)。篇文章我們將討論一下如何在現(xiàn)行的 Golang 框架下提供更友好和優(yōu)雅的錯(cuò)誤處理。需要的朋友們可以參考借鑒,下面來(lái)一起看看吧。
    2017-02-02
  • golang獲取客戶端ip的實(shí)現(xiàn)

    golang獲取客戶端ip的實(shí)現(xiàn)

    本文主要介紹了golang獲取客戶端ip的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07

最新評(píng)論