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