面试题答案
一键面试可能的输出结果
由于这是并发执行的代码,每次运行的输出顺序可能会有所不同,但大致的输出模式如下:
- 可能以
goroutine1 start
或goroutine2 start
开始,因为两个 goroutine 是并发启动的。 - 接着可能是另一个 goroutine 的
start
输出。 - 然后两个 goroutine 中的
for
循环内的inner defer
输出,由于defer
是后进先出(LIFO),所以goroutine1 inner defer 2
、goroutine1 inner defer 1
、goroutine1 inner defer 0
这样的顺序输出,goroutine2
同理,这部分输出顺序不定,取决于哪个 goroutine 先执行完for
循环。 - 之后是
goroutine1 middle defer
和goroutine2 middle defer
,这两个输出顺序不定。 - 再之后是
goroutine1 end
和goroutine2 end
,顺序不定。 - 最后是
goroutine1 defer
和goroutine2 defer
,顺序不定。 - 最后输出
main end
。
例如一种可能的输出:
goroutine1 start
goroutine2 start
goroutine1 inner defer 2
goroutine1 inner defer 1
goroutine1 inner defer 0
goroutine1 middle defer
goroutine1 end
goroutine1 defer
goroutine2 inner defer 2
goroutine2 inner defer 1
goroutine2 inner defer 0
goroutine2 middle defer
goroutine2 end
goroutine2 defer
main end
defer的执行顺序
- 在单个 goroutine 内:
defer
语句是按照后进先出(LIFO)的顺序执行的。例如在goroutine1
中,for
循环内最后定义的defer fmt.Printf("goroutine1 inner defer %d\n", i)
会最先执行,然后是defer fmt.Println("goroutine1 middle defer")
,最后是defer func() { fmt.Println("goroutine1 defer"); wg.Done() }()
。 - 在并发场景下:不同 goroutine 之间的
defer
执行顺序是不确定的,取决于 goroutine 的调度顺序。
可能出现的问题
- 资源泄漏:如果
defer
语句用于释放资源(如文件句柄、数据库连接等),而 goroutine 因为 panic 或未正确执行defer
就退出,可能会导致资源泄漏。 - 死锁:如果在
defer
语句中进行一些会阻塞的操作,并且这些操作依赖于其他 goroutine 释放资源,但其他 goroutine 也在等待当前 goroutine 执行defer
释放资源,就可能导致死锁。例如,如果在goroutine1 defer
中进行了一个阻塞操作,而这个操作需要goroutine2
释放某个资源,同时goroutine2 defer
中也有阻塞操作依赖goroutine1
释放资源,就会造成死锁。
需要注意的要点
- 资源管理:确保在
defer
中正确释放资源,特别是在可能发生 panic 的情况下。可以使用recover
在defer
函数中捕获 panic 并进行适当处理,保证资源释放。 - 并发安全:在
defer
语句中执行的操作要考虑并发安全,避免数据竞争。如果defer
操作涉及共享资源,需要使用适当的同步机制(如互斥锁等)。 - 避免阻塞:尽量避免在
defer
中进行长时间阻塞的操作,防止影响整个程序的并发性能和可能导致死锁。