面试题答案
一键面试使用recover机制面临的挑战
- 作用域限制:
recover
只能在defer
函数中生效,且只能捕获当前goroutine
中的panic
。如果panic
发生在其他goroutine
中,当前goroutine
无法直接使用recover
捕获。 - 并发控制问题:在多个
goroutine
并发执行时,很难确保所有goroutine
中的panic
都能被及时捕获和处理,可能导致部分goroutine
异常退出,影响整个程序的稳定性。 - 资源释放复杂:当
goroutine
发生panic
时,除了捕获错误,还需要正确释放该goroutine
占用的资源,否则可能导致资源泄漏。
设计方案
- 错误传递:通过通道在
goroutine
之间传递错误信息,使得主goroutine
能够统一处理错误。 - 资源管理:使用
defer
语句在goroutine
结束时释放资源,无论是否发生panic
。 - 监控和恢复:主
goroutine
监控所有子goroutine
的运行状态,当有goroutine
出现panic
时,能够及时捕获并采取相应的恢复措施。
关键代码片段
package main
import (
"fmt"
"sync"
)
func worker(id int, wg *sync.WaitGroup, errCh chan error) {
defer wg.Done()
defer func() {
if r := recover(); r != nil {
errCh <- fmt.Errorf("goroutine %d panicked: %v", id, r)
}
}()
// 模拟可能发生 panic 的操作
if id == 2 {
panic("simulated panic")
}
fmt.Printf("Goroutine %d is working\n", id)
}
func main() {
var wg sync.WaitGroup
errCh := make(chan error)
numWorkers := 3
for i := 1; i <= numWorkers; i++ {
wg.Add(1)
go worker(i, &wg, errCh)
}
go func() {
wg.Wait()
close(errCh)
}()
for err := range errCh {
fmt.Println(err)
}
fmt.Println("All goroutines have finished")
}
在上述代码中:
worker
函数:使用defer
配合recover
捕获goroutine
内部的panic
,并通过errCh
通道将错误信息传递出去。同时,使用defer wg.Done()
确保goroutine
结束时通知sync.WaitGroup
。main
函数:启动多个worker
goroutine
,并使用sync.WaitGroup
等待所有goroutine
完成。通过errCh
通道接收并处理goroutine
中传递过来的错误信息。当所有goroutine
完成后关闭errCh
通道,main
函数继续执行输出最终结果。这样设计可以有效地捕获并处理并发场景下的panic
,保证程序的稳定性和资源的合理释放。