這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
Go語言提供defer關鍵字,用於延遲調用,延遲到當函數返回前被執行,多用於資源釋放、解鎖以及錯誤處理等操作。比如:
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()}
如果一個函數內引用了多個defer,它們的執行順序是怎麼樣的呢?比如:
package聽mainfunc聽main()聽{defer聽println("a")defer聽println("b")}輸出:ba
如果函數中引入了panic函數,那麼延遲調用defer會不會被執行呢?比如:
func聽main()聽{聽聽聽聽defer聽println("a")聽聽聽聽panic("d")聽聽聽聽defer聽println("b")}輸出:apanic:聽dgoroutine聽1聽[running]:panic(0x48a560,聽0xc42000a340)/root/data/go/src/runtime/panic.go:500聽+0x1a1main.main()/root/data/workspace/src/defer/main.go:7聽+0x107exit聽status聽2
日常開發中,一定要記住defer是在函數結束時才被調用的,如果應用不合理,可能會造成資源浪費,給gc帶來壓力,甚至造成邏輯錯誤,比如:
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函數返回時才會執行的,不是在迴圈結束後執行,延遲調用,導致佔用資源聽聽聽聽聽聽聽聽//do聽stuff...聽聽聽聽}}
修改方案是直接調用Close函數或將邏輯封裝成獨立函數,比如:
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)聽聽聽聽//將商務邏輯獨立封裝成函數聽聽聽聽}}
在效能方面,延遲調用花費的代價也很大,因為這個過程包括註冊、調用等操作,還有額外的記憶體開銷。比如:
package聽mainimport聽"testing"import聽"fmt"import聽"sync"var聽m聽sync.Mutexfunc聽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/opestCap聽聽20000000,聽112聽ns/op,0聽allocs/op,聽0聽B/op
在要求高效能的高並發情境下,應避免使用延遲調用。
本文出自 “博學於文,約之於禮” 部落格,轉載請與作者聯絡!