golang 函数三 (延迟调用)

前端之家收集整理的这篇文章主要介绍了golang 函数三 (延迟调用)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

Go语言提供defer关键字,用于延迟调用,延迟到当函数返回前被执行,多用于资源释放、解锁以及错误处理等操作。比如:

funcmain(){
f,err:=createFile("defer.txt")
iferr!=nil{
fmt.Println(err.Error())
return
}
defercloseFile(f)
writeFile(f)
}

funccreateFile(filePathstring)(*os.File,error){
f,err:=os.Create(filePath)
iferr!=nil{
returnnil,err
}
returnf,nil
}

funcwriteFile(f*os.File){
fmt.Println("writefile")
fmt.Fprintln(f,"hellogopher!")
}

funccloseFile(f*os.File){
fmt.Println("closefile")
f.Close()
}

如果一个函数内引用了多个defer,它们的执行顺序是怎么样的呢?比如:

packagemain

funcmain(){
	deferprintln("a")
	deferprintln("b")
}
输出:
b
a


如果函数中引入了panic函数,那么延迟调用defer会不会被执行呢?比如:

funcmain(){
deferprintln("a")
panic("d")
deferprintln("b")
}
输出:
a
panic:d

goroutine1[running]:
panic(0x48a560,0xc42000a340)
	/root/data/go/src/runtime/panic.go:500+0x1a1
main.main()
	/root/data/workspace/src/defer/main.go:7+0x107
exitstatus2

日常开发中,一定要记住defer是在函数结束时才被调用的,如果应用不合理,可能会造成资源浪费,给gc带来压力,甚至造成逻辑错误,比如:

funcmain(){
fori:=0;i<10000;i++{
filePath:=fmt.Sprintf("/data/log/%d.log",i)
fp,err:=os.Open(filePath)
iferr!=nil{
continue
}
defeffp.Close()//这是要在main函数返回时才会执行的,不是在循环结束后执行,延迟调用,导致占用资源
//dostuff...
}
}

修改方案是直接调用Close函数或将逻辑封装成独立函数,比如:

funclogAnalisys(pstring){
fp,err:=os.Open(p)
iferr!=nil{
continue
}
defeffp.Close()
//dostuff
}

funcmain(){
fori:=0;i<10000;i++{
filePath:=fmt.Sprintf("/data/log/%d.log",i)
logAnalisys(filePath)//将业务逻辑独立封装成函数
}
}


性能方面,延迟调用花费的代价也很大,因为这个过程包括注册调用等操作,还有额外的内存开销。比如:

packagemain

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

varmsync.Mutex

functest(){
	m.Lock()
	m.Unlock()
}

functestCap(){
	m.Lock()
	deferm.Unlock()
}

funcBenchmarkTest(t*testing.B){
	fori:=0;i<t.N;i++{
		test()
	}
}

funcBenchmarkTestCap(t*testing.B){
	fori:=0;i<t.N;i++{
		testCap()
	}
}

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

在要求高性能的高并发场景下,应避免使用延迟调用

猜你在找的Go相关文章