Go学习笔记(二) | 我对recover的一点误解

在golang的官方介绍中是这么介绍Recover函数的。

1
Recover is a built-in function that regains control of a panicking goroutine. Recover is only useful inside deferred functions. During normal execution, a call to recover will return nil and have no other effect. If the current goroutine is panicking, a call to recover will capture the value given to panic and resume normal execution.

也就是说当一个协程发生panic时,recover函数会捕捉到panic同时恢复正常的顺序。

像如下这样的代码:

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
package main

import (
"fmt"
"log"
"time"
)

func main() {

ch := make(chan int, 10)

for i := 2; i > 0; i-- {
go func(i int) {
defer func() {
err := recover()
if err != nil {
log.Println(err)
}
}()
for val := range ch {
fmt.Println("---->", val, "Go", i)
if val%2 == 1 && i == 1 {
panic("BOOM BOOM")
}
time.Sleep(2 * time.Second)
}
}(i)
}

var i int
for {
ch <- i
time.Sleep(1 * time.Second)
fmt.Println(i, "<---")
i++
}
}

我们向通道中写入,两个协成在接收,同时设计一个协程在一定的时候panic。那么在panic后,再recover,那在该协程的管道还能接收么?

过去,我一直以为,recover函数会重新恢复,该协程会类似重启一般==。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ go run recover.go
----> 0 Go 2
0 <---
----> 1 Go 1
2018/01/21 21:52:54 BOOM BOOM
1 <---
----> 2 Go 2
2 <---
3 <---
----> 3 Go 2
4 <---
----> 4 Go 2
5 <---
6 <---
----> 5 Go 2
7 <---
8 <---
----> 6 Go 2
...

但从执行的结果看:

​ 只有一个协程在工作,另外一个还是挂了。这时,我才意识到,recover函数并没有恢复原有的协程。只是当该协程panic后会执行defer。而在defer中,recover函数将panic拦截下来了,不会向外面抛出,从而导致其他协程的执行并不受到影响。但,已经panic的协程还是挂了。