defer: 延迟调用
FILO 先进后出即使函数发生panic错误,也会执行支持匿名函数调用用于资源清理、文件关闭、解锁以及记录时间等操作与匿名函数配合,可在return后修改函数的计算结果 func main() { for i := 0; i < 3; i++ { defer fmt.Println(i) // 2 1 0 } for i := 0; i < 3; i++ { defer func() { fmt.Println(i) // 3 3 3 }() } }延迟调用在函数结束时调用,如果将其放到循环中,会造成资源浪费
func main() { for i := 0; i < 1000; i++ { path := fmt.Sprintf("%04d.txt", i) f, err := os.Open(path) if err != nil { log.Println(err) continue } defer f.Close() // do something } }优化:
func main() { do := func(i int) { path := fmt.Sprintf("%04d.txt", i) f, err := os.Open(path) if err != nil { log.Println(err) return } defer f.Close() // do something } for i := 0; i < 1000; i++ { do(i) } }相比直接用CALL汇编指令调用函数,延迟调用则须花费更大代价。这其中包括注册、调用等操作,还有额外的缓存开销。
var m sync.Mutex func call() { m.Lock() m.Unlock() } func deferCall() { m.Lock() defer m.Unlock() } func BenchmarkCall(b *testing.B) { for i := 0; i < b.N; i++ { call() } } func BenchmarkDeferCall(b *testing.B) { for i := 0; i < b.N; i++ { deferCall() } } $ go test -bench=. -v BenchmarkCall BenchmarkCall-4 94623559 12.5 ns/op BenchmarkDeferCall BenchmarkDeferCall-4 70848210 16.0 ns/op只延迟紧跟 defer 的函数,并将该函数参数拷贝值入栈存起来
func calc(idx string, a, b int) int { ret := a + b fmt.Println(idx, a, b, ret) return ret } func main() { a, b := 1, 2 defer calc("1", a, calc("10", a, b)) a = 0 defer calc("2", a, calc("20", a, b)) b = 1 } // Output: // 10 1 2 3 // 20 0 2 2 // 2 0 2 2 // 1 1 3 4defer 后有多个表达式,只保留最后需要执行的那一个
type Slice []int func NewSlice() Slice { return make([]int, 0) } func (s *Slice) Add(i int) *Slice { *s = append(*s, i) //fmt.Println(*s) return s } func test(s *Slice) { defer s.Add(1).Add(2) s.Add(3) fmt.Println(*s) // [1, 3] } func main() { s := NewSlice() test(&s) fmt.Println(s) // [1, 3, 2] }Golang中defer、return、返回值之间执行顺序坑:
多个defer的执行顺序为“后进先出”;defer、return、返回值三者的执行逻辑应该是:return最先执行,return负责将结果写入返回值中;接着defer开始执行一些收尾工作;最后函数携带当前返回值退出。 func func1(i int) (t int) { t = i defer func() { t += 3 }() return t } func func2(i int) int { t := i defer func() { t += 3 }() return t } func func3(i int) (t int) { defer func() { t += i }() // t 指向的对象 return 2 } func main() { fmt.Println(func1(1)) // 4 fmt.Println(func2(1)) // 1 fmt.Println(func3(1)) // 3 }其他类似示例:
// 1 func f1() (r int) { defer func() { r++ }() return 0 } // 1 func f2() (r int) { i := 1 defer func() { i++ }() return i } // 2 func f3() (r int) { defer func(r int) { r++ }(r) return 2 } // 3 func f4() (r int) { i := 1 defer func() { r = i + 2 }() return i }