Go by Example - Panic, Defer and Recover

Go by Example - Panic, Defer and Recover

介绍go中的panic defer和从错误中恢复

Panic

panic通常意味着出现了意想不到的错误。大多数情况下,我们用它来快速解决正常运行中不应该出现的错误,或者我们还没准备好从容应对的错误。

panic.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "os"

func main() {
// 我们将在整个网站中使用 panic 来检查意外错误
// 这是网站上唯一一个专门用于 panic 的程序
panic("a problem")

// panic 的一个常见用法是,当函数返回一个我们不知道如何(或不想)处理的错误值时,终止函数
// 下面是一个例子,如果我们在创建新文件时遇到意外错误,就会惊慌失措
_, err := os.Create("/tmp/file")
if err != nil {
panic(err)
}
}

运行该程序将导致程序崩溃,打印错误信息和 goroutine 跟踪,并以非零状态退出。
当 main 中的第一个 panic 启动时,程序会退出而不会进入代码的其他部分。如果你想看到程序尝试创建临时文件,请注释掉第一个 panic。

log
1
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.go
1
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"
)

// 假设我们想创建一个文件,向其中写入内容,然后在完成后关闭
// 下面是我们如何使用 defer 来做到这一点
func main() {
// 使用 createFile 获取文件对象后,我们会立即使用 closeFile 推迟关闭文件
// 这将在外层函数(main)结束后,即 writeFile 完成后执行
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)
}
}

运行程序确认文件在写入后已关闭。

log
1
2
3
4
$ go run defer.go
creating
writing
closing

Recover

Go 可以通过使用内置的恢复函数从恐慌中恢复过来。recovery 可以阻止 panic 终止程序,让程序继续执行。
举个有用的例子:如果其中一个客户端连接出现严重错误,服务器不会希望它崩溃。相反,服务器希望关闭该连接,继续为其他客户端提供服务。事实上,这正是 Go 的 net/http 默认为 HTTP 服务器做的事情。

recover.go
1
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"

// 这个函数会导致panic
func mayPanic() {
panic("a problem")
}

// recover 必须在递延函数中调用
// 当外层函数发生恐慌时,延缓函数将被激活,其中的恢复调用将捕捉到恐慌
func main() {
// recovery 的返回值是调用 panic 时出现的错误
defer func() {
if r := recover(); r != nil {

fmt.Println("Recovered. Error:\n", r)
}
}()

mayPanic()

// 这段代码不会运行,因为 mayPanic 会恐慌
// main 的执行会在恐慌发生时停止,并在延迟闭包中恢复
fmt.Println("After mayPanic()")
}
log
1
2
3
$ go run recover.go
Recovered. Error:
a problem

参考链接

作者

孤独小狼

发布于

2024-04-07

更新于

2024-04-07

许可协议

评论