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 main
func main() {
defer println("a")
defer println("b")
}
輸出:
b
a
如果函數中引入了panic函數,那麼延遲調用defer會不會被執行呢?比如:
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
日常開發中,一定要記住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 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