Golang关键字——defer

一、defer解决什么问题?

试想下,我们在一个函数中对临界区执行加锁操作,但是,临界区的代码可能会异常退出(如下图所示)。

为了防止这种情况,我们需要在每个err后面,都加上一个l.Unlock,这样的代码也难看了。为了解决这个问题,就需要这样的一个功能:能在函数、方法执行完毕后,进行一些收尾操作,这些操作可能是释放资源、或捕获panic错误,所以就有了defer这个关键字;

二、defer注意点

关于defer源码方面的信息,我觉得了解以下两点即可:

  • 知道defer函数会包装成一个_defer结构,挂载到相应的协程上;
  • 知道看汇编代码,找到defer的运行时函数runtime.deferreturn;

**【注】**没必要陷入看defer源码上实现上,了解以下这部分,就够了。如果开发过程中,有需求、有精力再去深究细节。

预参数计算

预参数计算,就是说再运行到defer函数的时候,它的参数就已经确定了。像下面这个例子打印出来的i为0

go 复制代码
func a() {
    i := 0
    defer fmt.Println(i)
    i++
    return
}

Go Playground

LILO

go 复制代码
func b() {
    for i := 0; i < 4; i++ {
        defer fmt.Print(i)
    }
}

LILO,表示的是当运行到defer的时候,runtimedefer函数打包成一个_defer挂在到当前协程的_defer字段。对于上述代码,goroutine的图如下所示。当函数执行完时,从后到前执行对应链表,4,3,2,1

修改函数具名返回值

go 复制代码
func c() (i int) {
    defer func() { i++ }()
    return 1
}

上述代码,输出2。原因是,函数返回的1,先复制具名函数i,然后执行i++。「这部分有疑问,可以看下Golang函数布局」

三、defer性能损耗

defer带来方便的同时,也会带来一定的性能损耗。这个我们可以通过Benchmark来验证下。我们只需要了解有损耗即可,这点损耗对于大部分业务来说是没有影响的;

go 复制代码
func sum(max int) int {
   total := 0
   for i := 0; i < max; i++ {
      total += i
   }

   return total
}

func fooWithDefer() {
   defer func() {
      sum(10)
   }()
}

func fooWithOutDefer() {
   sum(10)
}

func BenchmarkFooWithDefer(b *testing.B) {
   for i := 0; i < b.N; i++ {
      fooWithDefer()
   }
}

func BenchmarkFooWithOutDefer(b *testing.B) {
   for i := 0; i < b.N; i++ {
      fooWithOutDefer()
   }
}
go 复制代码
➜  defer git:(master) ✗ go test -bench .                         
goos: darwin
goarch: amd64
pkg: go-tool/basic/defer
BenchmarkFooWithDefer-12        172309779                6.91 ns/op
BenchmarkFooWithOutDefer-12     249897656                4.76 ns/op
PASS
ok      go-tool/basic/defer     4.080s

总结

本文主要描述关于defer对的以下几点:

  1. defer解决什么问题?
  2. defer三个重要的特性,分别是预参数计算、LILO、修改函数具名返回值;
  3. 通过Benchmark了解defer带来的性能损耗;

参考

相关推荐
潘多编程15 分钟前
Spring Boot微服务架构设计与实战
spring boot·后端·微服务
2402_8575893620 分钟前
新闻推荐系统:Spring Boot框架详解
java·spring boot·后端
2401_8576226622 分钟前
新闻推荐系统:Spring Boot的可扩展性
java·spring boot·后端
Amagi.2 小时前
Spring中Bean的作用域
java·后端·spring
2402_857589362 小时前
Spring Boot新闻推荐系统设计与实现
java·spring boot·后端
J老熊2 小时前
Spring Cloud Netflix Eureka 注册中心讲解和案例示范
java·后端·spring·spring cloud·面试·eureka·系统架构
Benaso2 小时前
Rust 快速入门(一)
开发语言·后端·rust
sco52822 小时前
SpringBoot 集成 Ehcache 实现本地缓存
java·spring boot·后端
原机小子3 小时前
在线教育的未来:SpringBoot技术实现
java·spring boot·后端
吾日三省吾码3 小时前
详解JVM类加载机制
后端