Golang–谈谈defer
1、defer
defer是gol中的一种延迟调用机制,defer后面的函数只有当前函数执行完毕后才能执行
package main
import "fmt"
func main() {
fmt.Println("1")
defer func() {
fmt.Println("2")
}()
fmt.Println("3")
}
// 输出
1
3
2
2、多个defer执行顺序
多个defer出现的时候,会按照顺序压栈,然后先进后出的顺序执行。
package main
import "fmt"
func f1() {
fmt.Println("1")
}
func f2() {
fmt.Println("2")
}
func f3() {
fmt.Println("3")
}
func main() {
fmt.Println("0")
defer f1()
defer f2()
defer f3()
fmt.Println("4")
}
输出:
0
4
3
2
1
3、defer函数的参数传递
defer后面函数的参数在defer入栈时候就决定了,如果传的是引用,值才可能会改变。
- 例1:值传递
package main
import "fmt"
func main() {
x := 1
defer func(i int){
fmt.Println(i) // 输出:1
}(x) // 值传递,只是拷贝副本,保存的是入栈那一刻的值
x++
}
- 例2:引用传递
package main
import "fmt"
func main() {
x := 1
defer func(i *int){
fmt.Println(i) // 输出:2
}(&x) // 引用传递,传的是变量地址
x++
}
4、defer与return顺序
首先看下return语句跟defer语句的区别:
package main
import "fmt"
func deferFunc() {
fmt.Println("defer func called")
}
func returnFunc() int {
fmt.Println("return func called")
return 0
}
func returnAndDefer() int {
defer deferFunc()
return returnFunc()
}
func main() {
returnAndDefer()
}
输出:
return func called
defer func called
5、defer与panic
- 当函数遇到panic,defer仍然会被执行。Go会先执行所有的defer链表(该函数的所有defer),当所有defer被执行完毕且没有recover时,才会进行panic。
- defer 最大的功能是 panic 后依然有效,所以defer可以保证你的一些资源一定会被关闭,从而避免一些异常出现的问题。
- 可以在defer中进行recover如果defer中包含recover,则程序将不会再进行panic,这就实现了Go中类似try-catch的机制。
package main
import "fmt"
func deferPanicFunc() {
defer func() {
fmt.Println("defer 处理资源关闭")
}()
panic("出错了")
}
func main() {
deferPanicFunc()
}
输出:
defer 处理资源关闭
panic: 出错了
goroutine 1 [running]:
main.deferPanicFunc()
- 可以在defer中recover
package main
import (
"fmt"
)
func deferPanicFunc() {
defer func() {
// 捕获异常
if err := recover(); err != nil {
fmt.Println("捕获异常")
} else {
fmt.Println("defer 处理资源关闭")
}
}()
// 抛出异常
panic("出错了")
}
func main() {
deferPanicFunc()
}
输出:
捕获异常
6、defer函数包含子函数
package main
import (
"fmt"
)
func f(index int, value int) int {
fmt.Println(index)
return index
}
func main() {
defer f(1, f(3, 0))
defer f(2, f(4, 0))
}
分析:
先把f1压栈,为了得到第二个参数,要执行f3;
再把f2压栈,为了得到第二个参数,执行f4;
在执行f1,f2。
输出:
3
4
2
1
func f(index int, value int) int {
fmt.Println(index)
return index
}
func main() {
defer f(1, f(3, 0))
defer f(2, f(4, 0))
}
分析:
先把f1压栈,为了得到第二个参数,要执行f3;
再把f2压栈,为了得到第二个参数,执行f4;
在执行f1,f2。
输出:
3
4
2
1