面试题答案
一键面试面临的问题
- 作用域限制:
recover
只能在defer
函数中生效,且仅能捕获当前 goroutine 中的panic
。在主 goroutine 中使用recover
无法捕获其他 goroutine 中发生的panic
,因为每个 goroutine 都有自己独立的调用栈,主 goroutine 的调用栈与其他 goroutine 的调用栈相互隔离。
正确处理方式
- 使用通道:
- 在子 goroutine 中,当
panic
发生时,通过recover
捕获panic
,然后将相关信息通过通道发送给主 goroutine。 - 主 goroutine 从通道接收这些信息并进行处理。
package main import ( "fmt" ) func main() { errorCh := make(chan interface{}) go func() { defer func() { if err := recover(); err != nil { errorCh <- err } }() // 模拟可能发生 panic 的操作 panic("子 goroutine 发生 panic") }() if err := <-errorCh; err != nil { fmt.Println("捕获到 panic:", err) } }
- 在子 goroutine 中,当
- 使用 sync.WaitGroup 结合通道:
- 如果需要等待多个 goroutine 完成,可以结合
sync.WaitGroup
。 - 每个子 goroutine 完成任务或者发生
panic
时,通过通道通知主 goroutine,并调用WaitGroup.Done()
。
package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup errorCh := make(chan interface{}) for i := 0; i < 3; i++ { wg.Add(1) go func(id int) { defer func() { if err := recover(); err != nil { errorCh <- fmt.Sprintf("goroutine %d 发生 panic: %v", id, err) } wg.Done() }() // 模拟可能发生 panic 的操作 if id == 1 { panic("特定 goroutine 发生 panic") } fmt.Printf("goroutine %d 正常运行\n", id) }(i) } go func() { wg.Wait() close(errorCh) }() for err := range errorCh { fmt.Println("捕获到 panic:", err) } }
- 如果需要等待多个 goroutine 完成,可以结合