Golang defer小结

it2023-06-06  74

Golang defer小结

1. 概念2. 循环3. 闭包4. 性能5. 复杂表达式6. 函数返回值

1. 概念

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 }() } }

2. 循环

延迟调用在函数结束时调用,如果将其放到循环中,会造成资源浪费

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) } }

3. 闭包

func main() { var fns = [3]func(){} for i := 0; i < 3; i++ { // 4th, defer i = 2, 1, 0 defer fmt.Println("defer i =", i) // 3rd, closure_without_arg_defer i = 3, 3, 3 defer func() { fmt.Println("closure_without_arg_defer i =", i) }() // 2nd, closure_with_arg_defer i = 2, 1, 0 defer func(i int) { fmt.Println("closure_with_arg_defer i =", i) }(i) fns[i] = func() { fmt.Println("closure i =", i) } } // 1st, but always "closure i = 3" for _, fn := range fns { fn() } }

4. 性能

相比直接用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

5. 复杂表达式

只延迟紧跟 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 4

defer 后有多个表达式,只保留最后需要执行的那一个

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] }

6. 函数返回值

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 }
最新回复(0)