面试题答案
一键面试- 程序输出结果:
- 程序首先会输出
worker defer
,然后程序会异常退出,不会输出nested go func defer
。
- 程序首先会输出
- defer在并发和异常处理场景下的执行逻辑:
- defer在正常函数中的执行逻辑:
- 在Go语言中,
defer
语句会将其后面跟随的函数推迟到包含该defer
语句的函数即将返回时(包括正常返回和通过panic
异常返回)执行。在worker
函数中,defer fmt.Println("worker defer")
会在worker
函数结束时执行。这里worker
函数执行到time.Sleep(2 * time.Second)
后,函数结束,所以会输出worker defer
。
- 在Go语言中,
- defer在并发场景下的执行逻辑:
- 当
worker
函数中启动了一个新的 goroutine(go func() {... }()
)时,这个新的 goroutine 与worker
函数是并发执行的。新的 goroutine 有自己独立的调用栈,与worker
函数的调用栈相互独立。
- 当
- defer在异常(panic)处理场景下的执行逻辑:
- 在新的 goroutine 中,当执行到
panic("nested panic")
时,该 goroutine 会发生恐慌(panic)。在这个 goroutine 内部,defer fmt.Println("nested go func defer")
应该在该 goroutine 因为panic
而结束时执行,但由于这个 goroutine 没有被recover
捕获,它会导致整个程序异常退出,没有机会执行nested go func defer
的defer
语句。而worker
函数本身不受这个新 goroutine 内部panic
的影响,依然按照自己的逻辑执行并输出worker defer
后结束。如果要执行nested go func defer
,需要在新的 goroutine 内部使用recover
来捕获panic
。例如:
- 在新的 goroutine 中,当执行到
- defer在正常函数中的执行逻辑:
package main
import (
"fmt"
"time"
)
func worker() {
defer fmt.Println("worker defer")
go func() {
defer func() {
if r := recover(); r!= nil {
fmt.Println("Recovered from:", r)
fmt.Println("nested go func defer")
}
}()
panic("nested panic")
}()
time.Sleep(2 * time.Second)
}
在上述修改后的代码中,新的 goroutine 内部通过 recover
捕获了 panic
,从而可以执行 nested go func defer
。