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带来的性能损耗;

参考

相关推荐
En^_^Joy13 小时前
Django项目配置全攻略:settings配置文件
后端·python·django
IT_陈寒16 小时前
Python线程池吞了异常还不告诉我,这谁顶得住啊
前端·人工智能·后端
小强库计算机毕业设计16 小时前
SpringBoot+Vue3 学生宿舍管理系统实战
java·spring boot·后端·vue·学生宿舍管理系统
小狼18316 小时前
实践6|SDD 实战:AI 写的规格被推翻了四次
后端·claude
篮框坏了16 小时前
"DeepSeek Harness 实测:一毛钱干三活,但默认配置是个坑"
后端·ai编程
jobBridge2116 小时前
大模型到底是怎么"想"的?我把 Transformer 拆开,发现它其实是个"接词狂魔"
人工智能·后端·编程语言
唐青枫17 小时前
看懂内存地址之后,才算真正入门 Zig:指针、切片与实战
后端
朋克洛德的码农17 小时前
Go并发-sync包四剑客:Mutex、RWMutex、WaitGroup、Once-从入门到原理
开发语言·后端·golang
fulton17 小时前
为什么不用现成的开源工具?NovelOps与6大AI写作工具横向对比
后端
fulton17 小时前
3个月踩坑实录:从想法到270章规划,AI写长篇到底要花多少成本?
后端