介绍go中的panic defer和从错误中恢复
Panic
panic通常意味着出现了意想不到的错误。大多数情况下,我们用它来快速解决正常运行中不应该出现的错误,或者我们还没准备好从容应对的错误。
panic.go1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package main
import "os"
func main() { panic("a problem")
_, err := os.Create("/tmp/file") if err != nil { panic(err) } }
|
运行该程序将导致程序崩溃,打印错误信息和 goroutine 跟踪,并以非零状态退出。
当 main 中的第一个 panic 启动时,程序会退出而不会进入代码的其他部分。如果你想看到程序尝试创建临时文件,请注释掉第一个 panic。
log1 2 3 4 5 6 7 8
| $ go run panic.go panic: a problem
goroutine 1 [running]: main.main() /.../panic.go:12 +0x47 ... exit status 2
|
需要注意的是,与某些使用异常来处理许多错误的语言不同,在 Go 中,尽可能使用错误指示返回值是习以为常的做法。
Defer
defer 通常用于确保在程序执行的稍后阶段执行函数调用,通常是为了进行清理。
defer.go1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| package main
import ( "fmt" "os" )
func main() { f := createFile("/tmp/defer.txt") defer closeFile(f) writeFile(f) }
func createFile(p string) *os.File { fmt.Println("creating") f, err := os.Create(p) if err != nil { panic(err) } return f }
func writeFile(f *os.File) { fmt.Println("writing") fmt.Fprintln(f, "data")
}
func closeFile(f *os.File) { fmt.Println("closing") err := f.Close()
if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(1) } }
|
运行程序确认文件在写入后已关闭。
log1 2 3 4
| $ go run defer.go creating writing closing
|
Recover
Go 可以通过使用内置的恢复函数从恐慌中恢复过来。recovery 可以阻止 panic 终止程序,让程序继续执行。
举个有用的例子:如果其中一个客户端连接出现严重错误,服务器不会希望它崩溃。相反,服务器希望关闭该连接,继续为其他客户端提供服务。事实上,这正是 Go 的 net/http 默认为 HTTP 服务器做的事情。
recover.go1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package main
import "fmt"
func mayPanic() { panic("a problem") }
func main() { defer func() { if r := recover(); r != nil {
fmt.Println("Recovered. Error:\n", r) } }()
mayPanic()
fmt.Println("After mayPanic()") }
|
log1 2 3
| $ go run recover.go Recovered. Error: a problem
|
参考链接