面试题答案
一键面试defer语句特性
- 延迟执行:defer语句后的函数会在包含该defer语句的函数返回前执行。例如:
package main
import "fmt"
func main() {
fmt.Println("开始")
defer fmt.Println("结束")
}
执行结果会先输出“开始”,最后输出“结束”。 2. 按后进先出(LIFO)顺序执行:如果函数中有多个defer语句,它们会按照后进先出的顺序执行。例如:
package main
import "fmt"
func main() {
defer fmt.Println("defer 1")
defer fmt.Println("defer 2")
fmt.Println("main")
}
输出结果为:
main
defer 2
defer 1
- 可用于资源清理:常用于关闭文件、数据库连接等资源的清理。例如:
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Open("test.txt")
if err != nil {
fmt.Println("打开文件错误:", err)
return
}
defer file.Close()
// 后续处理文件操作
}
潜在陷阱
- 闭包引用问题:defer语句中的函数如果使用了闭包,要注意闭包中变量的值变化。例如:
package main
import "fmt"
func main() {
for i := 0; i < 3; i++ {
defer func() {
fmt.Println(i)
}()
}
}
预期输出可能是0, 1, 2,但实际输出是3, 3, 3。这是因为闭包引用的是循环变量i
,当defer函数执行时,i
已经变为3。
2. goroutine中资源管理不当:如果在goroutine中使用defer进行资源管理,可能因为goroutine提前退出导致资源未正确清理。例如:
package main
import (
"fmt"
"os"
)
func worker() {
file, err := os.Open("test.txt")
if err != nil {
fmt.Println("打开文件错误:", err)
return
}
defer file.Close()
// 假设这里有一个可能导致goroutine提前退出的操作
panic("模拟错误")
// 后续文件操作代码
}
func main() {
go worker()
select {}
}
在这个例子中,由于panic
导致goroutine提前退出,文件可能未正确关闭。
避免陷阱的方法
- 解决闭包引用问题:通过传值方式避免引用同一变量。例如:
package main
import "fmt"
func main() {
for i := 0; i < 3; i++ {
index := i
defer func() {
fmt.Println(index)
}()
}
}
这样每次循环都会创建一个新的index
变量,确保defer函数引用的是正确的值,输出为0, 1, 2。
2. 正确处理goroutine中的资源管理:使用recover
来捕获panic
并正确清理资源。例如:
package main
import (
"fmt"
"os"
)
func worker() {
file, err := os.Open("test.txt")
if err != nil {
fmt.Println("打开文件错误:", err)
return
}
defer func() {
if r := recover(); r != nil {
fmt.Println("捕获到panic:", r)
file.Close()
}
}()
panic("模拟错误")
}
func main() {
go worker()
select {}
}
通过recover
捕获panic
,并在defer
中确保文件被正确关闭。