golang defer原理
这篇文章是系列文章的第一篇,系列文章主要包括:
- golang defer的原理
- golang panic和recover()函数的原理(包括golang对于错误处理方式)
- defer性能损耗的讨论以及最重要的应用场景
- defer在golang 1.13 上的性能
defer的特性
首先,我们通过一些实际的例子来看 defer 的特性。
1. 延迟调用
package main
import (
"fmt"
"log"
)
func main() {
defer log.Println("defer sim lou")
fmt.Println("ending")
}
输出结果:
ending
2019/10/22 22:50:32 defer sim lou
可以看到 defer 定义的函数是在 main 函数逻辑执行完了之后才执行的,也就是延迟调用。
2. 后进先出
package main
import (
"log"
"strconv"
)
func main() {
for i := 1; i <= 6; i++ {
defer log.Println("defer sim lou-" + strconv.Itoa(i) + ".")
}
log.Println("ending.")
}
输出结果:
2019/10/22 22:56:37 ending.
2019/10/22 22:56:37 defer sim lou-6.
2019/10/22 22:56:37 defer sim lou-5.
2019/10/22 22:56:37 defer sim lou-4.
2019/10/22 22:56:37 defer sim lou-3.
2019/10/22 22:56:37 defer sim lou-2.
2019/10/22 22:56:37 defer sim lou-1.
根据输出可以看到,defer 函数的执行顺序是 LIFO,也就是后进先出的。
3. defer 作用域
package main
import (
"log"
)
func main() {
func() {
defer log.Println("defer sim lou.")
}()
log.Println("main.ending.")
}
输出结果是:
2019/10/22 22:58:59 defer sim lou.
2019/10/22 22:58:59 main.ending.
可以看到 defer 的作用于是函数级别的, 也就是defer 生效在 defer 定义所在的函数栈。
4. 异常处理
package main
import (
"log"
)
func main() {
defer func() {
if e := recover(); e != nil {
log.Println("defer sim lou.")
}
}()
panic("throw panic")
}
输出结果是:
2019/10/23 23:45:28 defer sim lou.
性能测试
defer 不是免费的午餐,任何一次的 defer 都存在性能问题,这样一个简单的性能对比:
基于go 1.13:
package main
import (
"sync"
"testing"
)
var lock sync.Mutex
func NoDefer() {
lock.Lock()
lock.Unlock()
}
func Defer() {
lock.Lock()
defer lock.Unlock()
}
func BenchmarkNoDefer(b *testing.B) {
for i :=