1.defer基础应用:延迟调用,后进先出,异常处理
场景:打开连接/关闭连接,加锁/释放锁,打开文件/关闭文件
func main() {
for i := 0; i < 6; i++ {
defer log.Println("EDDYCJY" + strconv.Itoa(i) + ".")
}
log.Println("end.")
}
func main() {
for i := 0; i < 6; i++ {
defer log.Println("EDDYCJY" + strconv.Itoa(i) + ".")
}
log.Println("end.")
}
2.底层实现
1.协程记录defer信息,退出时调用。
①堆上分配(大部分情况):在堆上开辟一个sched.deferpool,遇到defer语句就将信息放进这个pool里面,在p结构体里面。
type p struct {
····
deferpool []*_defer // pool of available defer structs (see panic.go)
deferpoolbuf [32]*_defer
在函数退出的时候,从deferpool中取出执行。
问题:放在堆上,涉及到垃圾回收问题。
②:栈上分配:遇到defer语句,将信息放到栈上,函数返回的时候从栈中取出执行。
好处:没有堆上垃圾回收问题。
问题:栈上空间有限,所以不能最多只能保存一个defer信息。
2.将defer编码直接编至函数尾
①开放编码:如果defer语句在编译的时候就可以固定,就直接改写用户代码,将defer语句放入函数末尾,这种效果最好但是能优化的可能性最小。
3.recover与panic
panic
是 Go 中的一个内置函数,可用于停止程序的正常执行并开始 panic。当 panic 发生时,程序停止当前的执行并开始展开堆栈,执行所有延迟函数,直到到达堆栈顶部。然后,它会打印 panic 消息并退出。
recover
是 Go 中的另一个内置函数,可用于从 panic 中恢复。当在defer函数内部调用 recover
时,它会停止堆栈的展开并返回传递给 panic
函数的值。如果在延迟函数之外调用 recover
或者没有 panic 发生,它会返回 nil
。
以下是使用 panic
和 recover
的示例:
func main() {
defer func() {
if r := recover(); r != nil {
log.Println("recovered from panic:", r)
}
}()
var a []int
a[1] = 1
}
在此示例中,程序尝试为未初始化的切片分配一个值,从而导致 panic。但是,defer 函数从 panic 中恢复并记录一条消息,而不是使程序崩溃。