一、defer、panic、recover之间执行顺序
1.defer和panic的执行顺序
defer是后进先出,协程遇到panic时,遍历本协程的defer链表,并执行defer,如果没有遇到recover,遍历完本协程的defer链表后,向stderr跑出panic信息
package main
import "fmt"
func main () {
defer_func()
}
func defer_func() {
defer func () {
fmt.Println("1")
} ()
defer func () {
fmt.Println("2")
} ()
defer func () {
fmt.Println("3")
} ()
panic("error")
}
2.defer、panic和recover的执行顺序
如果遇到recover,返回recover处继续往下执行,不会抛出panic异常信息
package main
import "fmt"
func main () {
defer_func()
}
func defer_func() {
defer func () {
if err := recover(); err != nil {
fmt.Println("recover")
}
fmt.Println("1")
} ()
defer func () {
fmt.Println("2")
} ()
defer func () {
fmt.Println("3")
} ()
panic("error")
}
3.defer及其入参的执行顺序
这里,有4个函数,他们的index序号分别为1,2,3,4。
那么这4个函数的先后执行顺序是什么呢?
这里面有两个defer, 所以defer一共会压栈两次,先进栈1,后进栈2。 那么在压栈function1的时候,需要连同函数地址、函数形参一同进栈,那么为了得到function1的第二个参数的结果,所以就需要先执行function3将第二个参数算出,那么function3就被第一个执行。同理压栈function2,就需要执行function4算出function2第二个参数的值。然后函数结束,先出栈fuction2、再出栈function1.
所以顺序如下:
- defer压栈function1,压栈函数地址、形参1、形参2(调用function3) --> 打印3
- defer压栈function2,压栈函数地址、形参1、形参2(调用function4) --> 打印4
- defer出栈function2, 调用function2 --> 打印2
- defer出栈function1, 调用function1--> 打印1
package main
import "fmt"
func function(index int, value int) int {
fmt.Println(index)
return index
}
func main() {
defer function(1, function(3, 0))
defer function(2, function(4, 0))
}
二、defer源码解读
1.创建defer函数deferproc
获取当前go语言协程,创建新的defer结构体,defer结构体中保存函数地址和函数入参,加入协程的defer链表中(头插法)
2.执行defer函数deferreturn
获取当前协程的defer链表,顺序遍历defer结构体,取出defer函数跳转过去执行,因为deferproc是头插法,所以顺序遍历是逆序执行的