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的时候,runtime将defer函数打包成一个_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带来的性能损耗;

参考

相关推荐
深蓝AI15 小时前
JWT 不是登录方案:三种登录态架构怎么选
后端
starzy199015 小时前
深入理解Scala Map:从实现原理到工程实践
开发语言·后端·scala
卷无止境16 小时前
凌晨三点的绿灯
后端
excel17 小时前
基于 GPT 的自动发布文章接口方案:从一句话或一张图片生成并发布内容
前端·javascript·后端
风若飞18 小时前
Spring Ai 入门遇到问题1
java·后端·spring
AI入职记19 小时前
Spring AI 结构化输出:结构 100% 通过,严格准确率只有 57%
后端·ai编程
aZhe的全栈知识分享19 小时前
AI 还原 Figma 效果总达不到预期?先别急着让它写代码
前端·后端·面试
码事漫谈19 小时前
WorkBuddy 一直更新卡死?两个毛病,一套处理办法
后端
函数小陈19 小时前
阿里云部署DSH与RAM完成运维
前端·后端
aurora_zjj19 小时前
一次持续 3 年的 OpenResty 死循环排查:最终定位到 LuaJIT ARM64 的一条汇编指令
后端