1. 为什么需要 defer
在实际开发中,开发者经常需要创建资源,比如:数据库连接、读写文件等,为了在 函数执行完毕后,能够及时的释放资源,可利用 defer
延时机制来完成
package main
import (
"fmt"
)
func add(n1 int, n2 int) int{
defer fmt.Println("n1=", n1) // n1= 30
defer fmt.Println("n2=", n2) // n2= 40
res := n1 + n2
fmt.Println("add res=", res) // add res= 70
return res
}
func main() {
res := add(30, 40)
fmt.Println("main res=", res) // main res= 70
}
1.1 使用细节
- 当执行到 defer 时,暂不执行,会将 defer 后面的语句压入到独立的 defer 栈中;当
add
函数执行完成后,再从 defer 栈中按照先进后出的方式执行 defer 后面的语句 - 将 defer 后的语句放入到栈时,也会将 相关变量的值复制一份保存到栈中
package main
import (
"fmt"
)
func add(n1 int, n2 int) int{
defer fmt.Println("n1=", n1) // n1= 30
defer fmt.Println("n2=", n2) // n2= 40
n1++ // 31
n2++ // 41
res := n1 + n2
fmt.Println("add res=", res) //add res= 72
return res
}
func main() {
res := add(30, 40)
fmt.Println("main res=", res) //main res= 72
}
可以通过代码看到,对
n1
、n2
进行自增操作后,并没有影响到 defer 后语句的 n1
、n2
值的输出
- defer 释放资源
func testFile() {
file = openFile()
// 用于关闭文件资源
defer file.close()
}
func testDatabase() {
conn = openDatabase()
// 关闭数据库连接
defer conn.close()
}
2. 错误处理
- Go 中如何在程序发生异常错误时,可以将错误信息捕获到,以保证程序正常运行而不被中断,同时还可以将错误信息提示出来
- Go 中引入了 defer 、panic 和 recover 来处理异常。可以理解为:Go 中可以抛出一个
panic
的异常,然后在defer
中通过recover
捕获这个异常进行处理
package main
import (
"fmt"
)
func main() {
// 产生了错误异常信息后程序仍然能继续执行下去
fmt.Println( "divide result : ", divide(1, 0) )
fmt.Println( "divide result : ", divide(2, 1) )
}
func divide(a int, b int) int {
// 注意 defer + recover 必须同时使用
defer func() {
err := recover()
if err != nil {
fmt.Println("计算异常:", err)
}
}()
c := a / b
return c
}
recover 使用参考文章:
2.1 自定义错误
Go 中也支持自定义错误,使用 errors.New
和 panic
内置函数
errors.New("错误信息描述")
,返回一个error
类型的执行,表示一个错误panic
内置函数,接受一个interface {}
类型的值作为参数,也就是可接受任意类型数据,并退出程序
package main
import (
"fmt"
"errors"
)
func main() {
test()
fmt.Println("main 流程 ...")
}
func test() {
err := getError(0)
if err != nil {
panic(err)
}
fmt.Println("test() 继续执行 ...")
}
func getError(n int) error{
if (n == 1) {
return nil
} else {
return errors.New("sort方法异常")
}
}
3.使用细节
3.1 不是所有panic
都能通过recover
捕获
- 除 0 的场景
package main
import (
"fmt"
"time"
)
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println("testError 发生异常", err)
}
}()
// 像这种异常 recover 就无法捕获
i := 1 / 0
time.Sleep(time.Second)
for i:=0; i<5; i++ {
fmt.Println("main i=",i)
}
}
- 内存溢出场景
- map 并发读写
- 参考文章