defer的作用?
可以让defer后的语句在当前函数执行完成后执行。应用场景:在打开文件下一场使用defer避免忘记关闭。但是,defer具有短暂延迟,对于高性能的程序尽量避免使用。
defer的执行顺序?
代码执行遇到defer语句,将defer后的函数注册。外层函数退出后,函数内注册过的函数逆序执行(先注册、后执行)。当注册的函数为nil时,会panic。
defer后的函数引用外部变量有两种方式:
- 函数参数:函数参数分为值参数和引用参数。值参数,defer函数里的值和传入的一样。而引用参数,外部的变量改变时,defer函数里的也会变化。
- 闭包:defer函数使用到变量是会根据上下文确定变量的。
在return之后的defer语句无法完成注册。
return的拆解?
return 语句可以被分解成3个语句执行的:
- X = XXX 赋值
- 调用defer函数
- return
func f()(r int) {
defer func(r int) {
r := r + 1
}(r)
return 1
}
上述代码实际转化为下面的代码:
func f()(r intr) {
r = 1
func(r int) {
r := r + 1
}(r)
return
}
闭包是什么?
闭包=函数+引用环境,匿名函数也被称为闭包,闭包捕获的变量是引用传递。
defer如何配合recover?
需要再defer的函数中调用。
func main(){
defer recover()
panic(404)
}
不可以。
func main(){
defer func (){
recover()
}
panic(404)
}
可以。
defer函数的执行流程?
在G(goroutine)执行过程中,遇到defer后调用deferporc函数创建_defer(创建_defer首先G绑定的P的_defer pool取,如果没有再到全局的_defer pool中取。)结构体,以链表的形式挂在到G上。
在RET指令执行之前,会调用deferreturn函数,deferreturn执行完毕之前调用jumpderfer,jumpdefer会跳转到之前defered的函数。执行完毕,控制权再次转交给runtinme。deferreturn继续执行其他defered函数。
为什么无法从父goroutine中恢复字goroutine的panic?
每个goroutine都有自己独立的执行栈,不与其他goroutine共享数据。goroutine没有自己的ID、返回值。
但可以通过全局panic捕获实现。
var notifier chan interface{}
func startGlobalPanicCapturing(){
notifier = make(chan interface{})
go func() {
for {
select {
case r := <-notifier:
fmt.Println(r)
}
}
}()
}
func main() {
startGlobalPanicCapturing()
Go(func() {
a:=make([]int, 1)
fmt.Println(a[1])
})
}
func Go(f func()) {
defer func() {
if r := recover(); r != nil{
notifier<-r
}
}()
f()
}
使用上述方式恢复goroutine有局限性,需要强制f中的goroutine都用Go创建。而且也会遇到不能恢复panic,如:并发读写map。