溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

golang 函數(shù)三 (延遲調(diào)用)

發(fā)布時(shí)間:2020-08-05 22:14:29 來源:網(wǎng)絡(luò) 閱讀:1074 作者:100018 欄目:開發(fā)技術(shù)

Go語言提供defer關(guān)鍵字,用于延遲調(diào)用,延遲到當(dāng)函數(shù)返回前被執(zhí)行,多用于資源釋放、解鎖以及錯(cuò)誤處理等操作。比如:

func main() {
    f, err := createFile("defer.txt")
    if err != nil {
        fmt.Println(err.Error())
        return
    }   
    defer closeFile(f)
    writeFile(f)
}

func createFile(filePath string) (*os.File, error) {
    f, err := os.Create(filePath)
    if err != nil {
        return nil, err 
    }   
    return f, nil 
}

func writeFile(f *os.File) {
    fmt.Println("write file")
    fmt.Fprintln(f, "hello gopher!")
}

func closeFile(f *os.File) {
    fmt.Println("close file")
    f.Close()
}

如果一個(gè)函數(shù)內(nèi)引用了多個(gè)defer,它們的執(zhí)行順序是怎么樣的呢?比如:

package main

func main() {
	defer println("a")
	defer println("b")
}
輸出:
b
a


如果函數(shù)中引入了panic函數(shù),那么延遲調(diào)用defer會(huì)不會(huì)被執(zhí)行呢?比如:

func main() {
    defer println("a")
    panic("d")
    defer println("b")
}
輸出:
a
panic: d

goroutine 1 [running]:
panic(0x48a560, 0xc42000a340)
	/root/data/go/src/runtime/panic.go:500 +0x1a1
main.main()
	/root/data/workspace/src/defer/main.go:7 +0x107
exit status 2

日常開發(fā)中,一定要記住defer是在函數(shù)結(jié)束時(shí)才被調(diào)用的,如果應(yīng)用不合理,可能會(huì)造成資源浪費(fèi),給gc帶來壓力,甚至造成邏輯錯(cuò)誤,比如:

func main() {
    for i := 0;i < 10000;i++{
        filePath := fmt.Sprintf("/data/log/%d.log", i)
        fp, err := os.Open(filePath)
        if err != nil{
            continue
        }
        defef fp.Close()    //這是要在main函數(shù)返回時(shí)才會(huì)執(zhí)行的,不是在循環(huán)結(jié)束后執(zhí)行,延遲調(diào)用,導(dǎo)致占用資源
        //do stuff...
    }
}

修改方案是直接調(diào)用Close函數(shù)或?qū)⑦壿嫹庋b成獨(dú)立函數(shù),比如:

func logAnalisys(p string){
    fp, err := os.Open(p)
    if err != nil{
        continue
    }
    defef fp.Close()
    //do stuff
}

func main() {
    for i := 0;i < 10000;i++{
        filePath := fmt.Sprintf("/data/log/%d.log", i)
        logAnalisys(filePath)    //將業(yè)務(wù)邏輯獨(dú)立封裝成函數(shù)
    }
}


在性能方面,延遲調(diào)用花費(fèi)的代價(jià)也很大,因?yàn)檫@個(gè)過程包括注冊、調(diào)用等操作,還有額外的內(nèi)存開銷。比如:

package main

import "testing"
import "fmt"
import "sync"

var m sync.Mutex

func test(){
	m.Lock()
	m.Unlock()
}

func testCap(){
	m.Lock()
	defer m.Unlock()
}

func BenchmarkTest(t *testing.B){
	for i:= 0;i < t.N; i++{
		test()
	}
}

func BenchmarkTestCap(t *testing.B){
	for i:= 0;i < t.N; i++{
		testCap()
	}
}

func main(){
	resTest := testing.Benchmark(BenchmarkTest)
	fmt.Printf("BenchmarkTest \t %d, %d ns/op,%d allocs/op, %d B/op\n", resTest.N, resTest.NsPerOp(), resTest.AllocsPerOp(), resTest.AllocedBytesPerOp())
	resTest = testing.Benchmark(BenchmarkTestCap)
	fmt.Printf("BenchmarkTestCap \t %d, %d ns/op,%d allocs/op, %d B/op\n", resTest.N, resTest.NsPerOp(), resTest.AllocsPerOp(), resTest.AllocedBytesPerOp())
}
輸出:
BenchmarkTest 	 50000000, 27 ns/op,0 allocs/op, 0 B/op
estCap 	 20000000, 112 ns/op,0 allocs/op, 0 B/op

在要求高性能的高并發(fā)場景下,應(yīng)避免使用延遲調(diào)用。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI