go 源码中的 panic 与 throw
# 一、panic
- panic 是 go 语言中一个内置函数,用于终止当前 goroutine 并报告相关错误信息,panic 之后非 defer 的函数都无法执行;
- 在 go 源代码中,常见的有数组内存越界、空指针、关闭一个 nil 管道等,都会发生 panic,用户也可以自行调用 panic 函数;
- panic 是一种可以恢复的异常,可以通过 recover 函数捕获最近的一个 panic,并继续执行正常的流程,但是 recover 函数只能在 defer 函数中使用,并且只能 recover 当前 goroutine 的 panic,recover 捕获 panic 后,从捕获后的地方开始继续执行,panic 到 recover 之间的代码不会被执行;
- 使用
panic
和recover
的基本流程如下:
func main() {
defer func() {
if err := recover(); err != nil {
// 捕获了 panic,进行一些日志打印或报警
fmt.Println("发生了 panic:", err)
}
}()
// 模拟发生 panic
panic("发生了一个错误")
// 这行代码不会被执行
fmt.Println("这行代码不会被执行")
}
// output: 发生了 panic: 发生了一个错误
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- 当在 defer 函数值中也发生了 panic,recover 只能捕获最近的一个,如下:
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println("recover ", err)
}
}()
defer func() {
panic("aaa")
}()
panic("bbb")
}
// output: recover aaa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 二、throw
- throw 是 go 源码中一个函数,无法被用户调用,用于退出整个程序,并打印相应的 fatal msg;
- go 语言认为这是无法动态修复的崩溃,所以不能通过 recover 来捕获;
- 例如源码中,阻塞等待一个 nil 管道、解锁一个未加锁的锁、并发读写 map 等,都会通过 throw 抛出崩溃;
func main() {
var c chan int
select {
case c <- 1: // fatal error: all goroutines are asleep - deadlock!
}
}
1
2
3
4
5
6
2
3
4
5
6
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println("recovering: ", err)
return
}
fmt.Println("after recover")
}()
var m sync.Mutex
m.Unlock()// fatal: sync: unlock of unlocked mutex
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
func main() {
m := make(map[int]int)
wg := sync.WaitGroup{}
wg.Add(2)
go func(w *sync.WaitGroup) {
defer w.Done()
for {
m[1] = 2
}
}(&wg)
go func(w *sync.WaitGroup) {
defer w.Done()
for {
m[1] = 3
}
}(&wg)
// fatal error: concurrent map writes
wg.Wait()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 三、总结
- panic 抛出的异常能够在同一个 goroutine 被用户通过 defer 中的 recover 捕获,并继续 panic 之后的逻辑,用户也可以在业务逻辑中自行 panic 和 recover;
- throw 抛出的崩溃无法通过 recover 的方式捕获,go 语言认为这是不应该恢复或是无法恢复的异常;
编辑 (opens new window)
上次更新: 2024/01/28, 18:04:03