Go defer的运行时机和遇到的坑

马谦马谦马谦
马谦马谦马谦
马谦马谦马谦
611
文章
12
评论
2017年11月29日22:03:44 评论

一、defer用法

defer 是golang中独有的流程控制语句,用于延迟指定语句的运行时机,运行于函数的内部,当他所属函数运行完之后它才会被调用。

例如以下使用defer的代码:

输出结果为:

它会先打印出HelloWorld ,然后再打印出HelloDefer。一个函数中如果有多个defer ,运行顺序和函数中的调用顺序相反,因为它们都是被写在了栈中:

运行结果:

二、defer和return

在包含有return语句的函数中,defer的运行顺序位于return之后,但是defer所运行的代码片段会生效:

运行结果:

这里很明显就能看到defer是在return之后运行的!但是有一个问题是defer里执行了语句i += 1 ,按照这个逻辑的话返回的i 值应该是2 而不是1 。这个问题是由于return的运行机制导致的:return返回一个对象时,如果返回类型不是指针或者引用类型,那么return返回的就不是这个对象本身,而是这个对象的副本。也就是说,实际上当defer执行的时候,return已经把值返回了,返回的是一个副本值,此时defer对值的修改就无效了。

如果返回的是引用值,defer的改动是有效的。我们可以把返回值得int类型改成*int:

程序的输出为:

可以看到,在return和defer执行前,i的值都是1,并且地址一样。返回到main函数后,i的地址也是和函数体的一样,但是值是2了,这就说明defer中对i的修改生效了。

三、defer和panic

panic会在defer运行完之后才把恐慌扩散到其他函数:

结果:

四、defer和for循环

不要在defer内使用外部变量,可能会造成一些意想不到的错误:

它的输出的结果是55555 ,原理很简单,因为defer会在for循环运行完后才会调用,for循环运行完时i的值为5,所以打印的i值会是55555。

正确的做法是不要在defer函数中使用共享变量,而是手动传入参数:

此时就会打印出43210而不是55555了。

历史上的今天
十一月
29
马谦马谦马谦
  • 本文由 发表于 2017年11月29日22:03:44
  • 转载请务必保留本文链接:https://www.dyxmq.cn/program/code/golang/golang-defer.html
国内几个优质的Go Module代理仓库服务 Golang

国内几个优质的Go Module代理仓库服务

一、简介 go module公共代理仓库,代理并缓存go模块。你可以利用该代理来避免DNS污染导致的模块拉取缓慢或失败的问题,加速你的构建。 简单来说就是国内访问被墙,go get无法在线获取到仓库,...
Golang中glog库的使用方法 Golang

Golang中glog库的使用方法

一、关于glog库 golang中的glog库是google著名开源C++日志库glog的golang版本,在golang默认日志库的基础上做了更进一层的封装使得该库能更贴近日常使用。项目首页为gol...
Golang等待组sync.WaitGroup的用法 Golang

Golang等待组sync.WaitGroup的用法

一、waitgroup介绍 多线程编程中,经常会遇到这样的一种场景:main函数中为了等待其他线程执行完,在return之前都要执行sleep以争取更多的时间给其他线程执行。例如: package m...
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: