Skip to content

Commit 64a2b7a

Browse files
committed
update panic, recover
1 parent efc6c51 commit 64a2b7a

File tree

3 files changed

+160
-3
lines changed

3 files changed

+160
-3
lines changed

readme.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040

4141
* [lesson19: goroutine和channel](./workspace/lesson19)
4242

43-
* [lesson20: defer](./workspace/lesson20)
43+
* [lesson20: defer语义](./workspace/lesson20)
4444

4545
* [lesson21: 并发编程之sync包介绍sync.WaitGroup](./workspace/lesson21)
4646

@@ -56,7 +56,7 @@
5656

5757
* [lesson27: 包Package](./workspace/lesson27)
5858

59-
* [lesson28: 错误处理error, panic, recover](./workspace/lesson28)
59+
* [lesson28: panic, recover运行期错误处理](./workspace/lesson28)
6060

6161

6262

workspace/lesson28/readme.md

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,130 @@
1-
# 错误处理
1+
# panic和recover
22

3+
panic和recover是Go的2个内置函数,用于程序运行期抛出异常(panic的功能)和处理异常(recover的功能)。
4+
5+
## panic
6+
7+
引发panic可以是程序显示调用panic函数,也可以是运行期错误,比如数组越界访问,除0等。
8+
9+
* 定义
10+
11+
```go
12+
func panic(interface{}) // 入参是一个空interface,无返回值
13+
```
14+
15+
* 示例
16+
17+
```go
18+
panic(12)
19+
panic("invalid parameter")
20+
panic(Error("cannot parse"))
21+
```
22+
23+
* 如果在函数F里,显示调用了panic或者函数F执行过程中出现运行期错误,那F的执行会终止,接下来会有以下行为依次产生:
24+
25+
* F里被defer的函数会执行。
26+
* F的上一级函数,也就是调用F的函数,假设是函数E。对函数E而言,F的调用就类似调用了panic,函数E里被defer的函数被执行
27+
* 如果函数E还有上一级函数,就继续往上,每一级函数里被defer的函数都被执行,直到没有上一级函数。
28+
* 经过了以上步骤,panic的错误就会被抛出来,整个程序结束。
29+
30+
* 既然可以用panic来抛运行期异常,那就有相应办法可以捕获异常,让程序正常往下执行。Go通过结合内置函数recover和defer语义,来实现捕获运行期异常。
31+
32+
## recover
33+
34+
recover是Go的内置函数,可以捕获panic异常。程序正常执行过程中,调用recover函数会返回nil,除此之外,没有其它任何效果。如果当前goroutine触发了panic,可以在代码的适当位置调用recover函数捕获异常,让程序继续正常执行,而不是异常终止。
35+
36+
* 定义
37+
38+
```go
39+
func recover() interface{} // 返回值是一个空interface,无入参
40+
```
41+
42+
* 示例
43+
44+
```go
45+
package main
46+
47+
import (
48+
"fmt"
49+
)
50+
51+
func a() {
52+
defer func() {
53+
/*捕获函数a内部的panic*/
54+
r := recover()
55+
fmt.Println("panic recover", r)
56+
}()
57+
panic(1)
58+
}
59+
60+
func main() {
61+
defer func() {
62+
/*因为函数a的panic已经被函数a内部的recover捕获了
63+
所以main里的recover捕获不到异常,r的值是nil*/
64+
r := recover()
65+
fmt.Println("main recover", r)
66+
}()
67+
a()
68+
fmt.Println("main")
69+
}
70+
71+
```
72+
73+
上例的执行结果是:
74+
75+
```go
76+
panic recover 1
77+
main
78+
main recover <nil>
79+
```
80+
81+
82+
83+
* recover在以下几种情况返回nil
84+
85+
* panic的参数是nil。这种情况recover捕获后,拿到的返回值也是nil。
86+
* goroutine没有panic产生。没有panic,那当然recover拿到的也就是nil了。
87+
* recover不是在被defer的函数里面被调用执行。recover必须结合defer一起使用才能生效。
88+
89+
* 一个更复杂的示例
90+
91+
```go
92+
package main
93+
94+
import "fmt"
95+
96+
func main() {
97+
f()
98+
fmt.Println("Returned normally from f.")
99+
}
100+
101+
func f() {
102+
defer func() {
103+
if r := recover(); r != nil {
104+
fmt.Println("Recovered in f", r)
105+
}
106+
}()
107+
fmt.Println("Calling g.")
108+
g(0)
109+
fmt.Println("Returned normally from g.")
110+
}
111+
112+
func g(i int) {
113+
if i > 3 {
114+
fmt.Println("Panicking!")
115+
panic(fmt.Sprintf("%v", i))
116+
}
117+
defer fmt.Println("Defer in g", i)
118+
fmt.Println("Printing in g", i)
119+
g(i + 1)
120+
}
121+
```
122+
123+
大家可以下载[recover2.go](./recover2.go)代码,本地运行看看结果是否和预期相符。
124+
125+
126+
127+
## References
128+
129+
* https://golang.google.cn/ref/spec#Run_time_panics
130+
* https://go.dev/blog/defer-panic-and-recover

workspace/lesson28/recover2.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package main
2+
3+
import "fmt"
4+
5+
func main() {
6+
f()
7+
fmt.Println("Returned normally from f.")
8+
}
9+
10+
func f() {
11+
defer func() {
12+
if r := recover(); r != nil {
13+
fmt.Println("Recovered in f", r)
14+
}
15+
}()
16+
fmt.Println("Calling g.")
17+
g(0)
18+
fmt.Println("Returned normally from g.")
19+
}
20+
21+
func g(i int) {
22+
if i > 3 {
23+
fmt.Println("Panicking!")
24+
panic(fmt.Sprintf("%v", i))
25+
}
26+
defer fmt.Println("Defer in g", i)
27+
fmt.Println("Printing in g", i)
28+
g(i + 1)
29+
}

0 commit comments

Comments
 (0)