在释放局部资源时, 可以用defer管理
Go语言版本基于defer
的Mutex
用法
func safeRead(Mutex *mu) []byte { mu.Lock() defer mu.Unlock() return read(); }
对于可能申请失败的资源也很好处理:
func loadFile(name string) ([]byte, error) { f, err := os.Open(name) if err != nil { return nil, err } defer f.Close() return load(f) }
使用defer
语句, 可以方便地组合函数/闭包和资源对象. 即使panic
时, defer
也能保证资源的正确释放.
defer
官方给出的文档上介绍defer的执行有三条基本规则:
1. defer函数是在外部函数return后,按照后申明先执行(栈)的顺序执行的;
package main import "fmt" func main() { defer fmt.Println("1") defer fmt.Println("2") defer fmt.Println("3") }
输出是
3
2
1
2. defer函数的参数的值,是在申明defer时确定下来的;
看几个例子吧,注意第一条规则。
这是一个普通类型的例子:
package main import "fmt" func main() { i := 0 defer fmt.Println(i) // 这也算是作为defer函数的参数 defer func(j int) { fmt.Println(j) }(i) // 作为参数 defer func() { fmt.Println(i) }() // 作为闭包(closure)进行引用 i++ }
输出是:
1
0
0
如果是引用类型也是一样的道理:
- 当修改引用对象的属性时:
-
package main import "fmt" type Person struct { name string } func main() { person := &Person{"Lilei"} defer fmt.Println(person.name) // person.name作为普通类型当做defer函数的参数 defer fmt.Printf("%v ", person) // 引用类型作为参数 defer func(p *Person) { fmt.Println(p.name) }(person) // 同上 defer func() { fmt.Println(person.name) }() // 闭包引用,对引用对象属性的修改不影响引用 person.name = "HanMeimei" }
输出是:
HanMeimei HanMeimei &{HanMeimei} Lilei
- 当修改引用本身时:
package main import "fmt" type Person struct { name string } func main() { person := &Person{"Lilei"} defer fmt.Println(person.name) // 同上,person.name作为普通类型当做defer函数的参数 defer fmt.Printf("%v ", person) // 作为defer函数的参数,申明时指向“Lilei” defer func(p *Person) { fmt.Println(p.name) }(person) // 同上 defer func() { fmt.Println(person.name) }() // 作为闭包引用,随着person的改变而指向“HanMeimei” person = &Person{"HanMeimei"} }
输出是:
HanMeimei
Lilei
&{Lilei}
Lilei
在defer函数申明时,对外部变量的引用是有两种方式的,分别是作为函数参数和作为闭包引用。作为函数参数,则在defer申明时就把值传递给defer,并被cache起来。作为闭包引用的话,则会在defer函数执行时根据整个上下文确定当前的值。
3. defer函数可以读取和修改外部函数申明的返回值。
同样给一个例子
package main import "fmt" func main() { fmt.Printf("output: %d ", f()) } func f() (i int) { defer fmt.Println(i) // 参数引用 defer func(j int) { fmt.Println(j) }(i) // 同上 defer func() { fmt.Println(i) }() // 闭包引用 defer func() { i++ }() // 执行前,i=2 defer func() { i++ }() // 执行前,i=1 i++ return }
有了之前的基础,这个输出可以轻松推测出来:
3 0 0 output: 3
defer的基本规则就这三条了。了解之后,瞬间明朗了。
最开始的答案
通过defer的三条规则(其实只要前两条)就能知道输出了:
3
3
3
0