[Golang] Possible resource leak, 'defer' is called in the 'for' loop
今天被goland警告在golang的for循环中使用defer可能会导致资源泄漏,主要是因为defer
的作用域是一个函数,不是一个语句块。
测试如下:
go
func main() {
for i := 0; i < 3; i++ {
fmt.Println("before:", i)
defer fmt.Println("defer:", i)
fmt.Println("after:", i)
}
}
输出为
bash
before: 0
after: 0
before: 1
after: 1
before: 2
after: 2
defer: 2
defer: 1
defer: 0
defer并不会在一次for循环执行完成后执行defer,而是会在整个函数体执行完成后执行defer。且defer的执行顺序为先进后出,类似堆栈。
解决方法:
将defer放在函数中执行。如下:
go
func main() {
for i := 0; i < 3; i++ {
loopBody(i)
}
}
// defer会在loopBody(i int)执行完毕后再执行
func loopBody(i int) {
fmt.Println("before:", i)
defer fmt.Println("defer:", i)
fmt.Println("after:", i)
}
注意,要点在于不能使defer关键字出现在for循环中。使用匿名函数依然不可行,如下:
go
// 所有defer会在main()执行完毕后再执行
func main() {
for i := 0; i < 3; i++ {
fmt.Println("before:", i)
defer func(i int) {fmt.Println("defer:", i)}(i)
fmt.Println("after:", i)
}
}
匿名函数更不可以写成如下方式:
go
// 所有defer会在main()执行完毕后再执行,且均输出defer: 3
func main() {
for i := 0; i < 3; i++ {
fmt.Println("before:", i)
defer func() {fmt.Println("defer:", i)}() // 注意此处函数不接受参数
fmt.Println("after:", i)
}
}
完毕。