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,它们的执行顺序是怎么样的呢?比如:
packagemainfuncmain(){deferprintln("a")deferprintln("b")}输出:ba
如果函数中引入了panic函数,那么延迟调用defer会不会被执行呢?比如:
funcmain(){deferprintln("a")panic("d")deferprintln("b")}输出:apanic:dgoroutine1[running]:panic(0x48a560,0xc42000a340)/root/data/go/src/runtime/panic.go:500+0x1a1main.main()/root/data/workspace/src/defer/main.go:7+0x107exitstatus2
日常开发中,一定要记住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)//将业务逻辑独立封装成函数}}
在性能方面,延迟调用花费的代价也很大,因为这个过程包括注册、调用等操作,还有额外的内存开销。比如:
packagemainimport"testing"import"fmt"import"sync"varmsync.Mutexfunctest(){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,%dns/op,%dallocs/op,%dB/op\n",resTest.N,resTest.NsPerOp(),resTest.AllocsPerOp(),resTest.AllocedBytesPerOp())}输出:BenchmarkTest50000000,27ns/op,0allocs/op,0B/opestCap20000000,112ns/op,0allocs/op,0B/op
在要求高性能的高并发场景下,应避免使用延迟调用。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。