每次都被 defer
,panic
和recover
坑的死去活来,今天抽出时间来整理一下。
1. defer
1.1 匿名和有名返回值(defer能改有名)
- 如果是匿名返回值,执行Return语句后,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
| package main
import ( "fmt" )
func main() { fmt.Println("a return:", a()) }
func a() int { var i int defer func() { i++ fmt.Println("a defer2:", i) }() defer func() { i++ fmt.Println("a defer1:", i) }() return i }
|
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
| package main
import ( "fmt" )
func main() { fmt.Println("b return:", b()) }
func b() (i int) { defer func() { i++ fmt.Println("b defer2:", i) }() defer func() { i++ fmt.Println("b defer1:", i) }() return i }
|
1.2 defer 参数会提前算
defer声明时会先计算确定参数的值,defer推迟执行的仅是其函数体。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package main
import ( "fmt" "time" )
func main() { defer P(time.Now()) time.Sleep(1e9) fmt.Println("1", time.Now()) } func P(t time.Time) { fmt.Println("2", t) fmt.Println("3", time.Now()) }
|
1.3 调用os.Exit不会执行defer
当发生panic时,所在goroutine的所有defer会被执行,但是当调用os.Exit()方法退出程序时,defer并不会被执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package main
import ( "fmt" "os" )
func deferExit() { defer func() { fmt.Println("defer") }() os.Exit(0) } func main() { deferExit() }
|
1.4 defer面试题
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"
func DeferFunc1(i int) (t int) { t = i defer func() { t += 3 }() return t }
func DeferFunc2(i int) int { t := i defer func() { t += 3 }() return t }
func DeferFunc3(i int) (t int) { defer func() { t += i }() return 2 }
func DeferFunc4() (t int) { defer func(i int) { fmt.Println(i) fmt.Println(t) }(t) t = 1 return 2 }
func main() { fmt.Println(DeferFunc1(1)) fmt.Println(DeferFunc2(1)) fmt.Println(DeferFunc3(1)) DeferFunc4() }
|
执行顺序应该是:1. return最先给返回值赋值(有名返回值直接赋值,匿名返回值则先声明再赋值);2. 接着defer开始执行一些收尾工作;3. 最后RET指令携带返回值退出函数。
2. panic
2.1 截获 panic
- defer 和 recover 配合, 并且先声明defer
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
| package main
import ( "fmt" )
func main() { defer func() { fmt.Println("a") if err := recover(); err != nil { fmt.Println(err) } fmt.Println("b") }()
panic("异常信息")
fmt.Println("c") }
|
2.2 panic 传递
panic 会一直传递, 导致主 gorouting 奔溃
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
| package main
import ( "fmt" "time" )
func testPanic2() { panic("testPanic panic2") }
func testPanic1() { go testPanic2() }
func main() { fmt.Println("begin") go testPanic1() for { time.Sleep(time.Second) } }
|
3. recover
3.1 截获 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
| package main
import ( "fmt" "time" )
func main() { defer func() { if err := recover(); err != nil { fmt.Println(err) } }()
go func() { panic("falut1") }()
time.Sleep(time.Second) }
|