这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记
在青训营的课程中,遇到了很多使用defer语句的部分,但并没有对这个语句进行深入讲解,本篇笔记将详细讲述一下defer语句。
defer语句在使用过程中往往成对出现,伴随着文件的打开关闭,网络连接与断开,加锁解锁。defer语句保证了无论是正常结束还是发生宕机,都可以执行,以此保证了在任何情况下不会因为疏忽而导致资源无法释放。
defer语句本身没有次数限制,多个defer存在的情况下回按defer语句顺序倒序执行.
接下来是一个涉及defer的问题,打印的结果是什么
func incr(a int) int {
var b int
defer func() {
a++
b++
}()
a++
b=a
return b
}
func main() {
var a, b int
b = incr(a)
fmt.Println(a, b)
这里涉及一个问题,就是“给返回值赋值”和“执行defer语句”这两个步骤哪个在先哪个在后? 答案是先给返回值赋值,所以在上述代码中,最后的打印结果是0,1 incr函数中,在return之前,incr函数先将b的值赋值给返回值,也就是1,之后执行defer语句,但这里defer语句并没有作用于返回值,所以结果仍然是1 接下来改一下上述代码:
func incr(a int) (b int) {
defer func() {
a++
b++
}()
a++
return a
}
func main() {
var a, b int
b = incr(a)
fmt.Println(a, b)
在上述代码的incr函数中,return语句将a的结果赋值给返回值b,也就是1,之后执行defer语句,返回值b再次自增,也就是2,所以此时打印的结果是0,2
通过上述两个代码块,总结下来便是,defer语句的执行顺序位于:给返回值赋值-> defer语句 -> 返回值返回
涉及defer的部分还有一个知识点,就是宕机的恢复,在java中可以通过try catch语句来捕获异常,在go中也有类似的语句,但是不如java那样随意自如的可以使用try catch,因为go提倡通过多返回值来返回error,而不是通过宕机来处理。
在go中,可以在defer语句中调用recover函数,并且包含这个defer语句的函数发生宕机,recover函数会终止当前的宕机状态并返回宕机的值。函数本身不会从之前宕机的地方继续运行而是正常返回,且recover函数只能在defer语句中进行调用,只在当前goroutine生效