面试题答案
一键面试保证程序优雅处理panic避免崩溃的方法
在Go语言中,使用defer
和recover
机制可以捕获并处理panic
,防止程序崩溃。在并发场景下,需要在每个可能触发panic
的goroutine
中使用defer
和recover
。
以下是一个示例代码:
package main
import (
"fmt"
)
func worker(id int) {
defer func() {
if r := recover(); r != nil {
fmt.Printf("Goroutine %d caught panic: %v\n", id, r)
}
}()
// 模拟可能触发panic的操作
if id == 2 {
panic("simulated panic")
}
fmt.Printf("Goroutine %d is working\n", id)
}
func main() {
for i := 1; i <= 3; i++ {
go worker(i)
}
// 防止main函数退出
select {}
}
在上述代码中,每个worker
goroutine
都使用了defer
和recover
。defer
函数在worker
函数结束时执行,recover
用于捕获panic
。如果goroutine
触发了panic
,recover
会捕获到并进行相应处理,避免程序崩溃。
并发场景下使用recover的陷阱及避免方法
- 陷阱:recover只能在defer函数中生效
如果不在
defer
函数中调用recover
,它将返回nil
,无法捕获panic
。例如:
package main
import (
"fmt"
)
func main() {
if r := recover(); r != nil {
fmt.Printf("Caught panic: %v\n", r)
}
panic("test panic")
}
上述代码中recover
不在defer
函数中,所以无法捕获panic
,程序依然会崩溃。
- 陷阱:无法跨
goroutine
捕获panic
recover
只能捕获当前goroutine
中的panic
,无法捕获其他goroutine
的panic
。例如:
package main
import (
"fmt"
)
func main() {
defer func() {
if r := recover(); r != nil {
fmt.Printf("Caught panic: %v\n", r)
}
}()
go func() {
panic("test panic in goroutine")
}()
// 防止main函数退出
select {}
}
上述代码中,main
函数中的recover
无法捕获到新goroutine
中的panic
,程序依然会崩溃。
- 避免方法
- 确保
recover
在defer
函数中调用,以便在函数结束时捕获panic
。 - 在每个可能触发
panic
的goroutine
内部使用defer
和recover
,而不是在调用goroutine
的外部尝试捕获panic
。这样可以确保每个goroutine
都能独立处理自身的panic
,避免程序因某个goroutine
的panic
而崩溃。
- 确保