介绍
defer是Golang中的一大特色,它被称为“延迟函数调用”,关于defer的底层比较复杂,暂时还未深入分析。
此篇未完待续,陆续增加。
用法
语法上,一个defer语句就是一个普通的函数或方法调用,在调用之前加上关键字defer。函数和参数表达式会在语句执行时求值,但是无论是正常情况下,执行return语句或者函数执行完毕,还是不正常的情况下,比如发生宕机,实际的调用推迟到包含defer语句的函数结束后才执行。defer语句没有限制使用次数;执行的时候以调用defer语句顺序的倒序进行(defer执行栈,先进后出)。
示例
package main
import "fmt"
func Plus() int {
i := 0
defer func() {
i ++
fmt.Printf("defer1:%d\n",i)
}()
defer func() {
i++
fmt.Printf("defer2:%d\n",i)
}()
return i
}
func main() {
fmt.Printf("Plus():%d\n",Plus())
}
//输出
//defer2:1
//defer1:2
//Plus():0
可以看到,defer语句是在Plus函数执行return i语句之后开始执行,并按照defer栈的方式依次执行。
注意:defer表达式中可以修改函数中的命名返回值。
我们如果将上述代码稍做修改,Plus返回结果也就不一样了。
package main
import "fmt"
//命名结果变量
func PlusName() (i int) {
i = 0
defer func() {
i ++
fmt.Printf("defer1:%d\n",i)
}()
defer func() {
i++
fmt.Printf("defer2:%d\n",i)
}()
return i
}
func main() {
fmt.Printf("PlusName():%d\n",PlusName())
}
//输出
//defer2:1
//defer1:2
//PlusName():2
常用场景
defer语句的应用场景往往在成对的操作上,比如文件打开关闭,网络连接断开,加锁解锁。
- 文件关闭
func ReadFile(filename string) ([]byte, error) {
f , err := os.Open(filename)
if err != nil{
return nil, err
}
defer f.Close()
return ioutil.ReadAll(f)
}
- 网络断开
func Fetch(url string) ([]byte, error){
resp, err := http.Get(url)
if err!= nil{
return nil, err
}
defer resp.Body.Close()
return ioutil.ReadAll(resp.Body)
}
- 解锁
type Server struct {
Address string
MaxCount int
CurrentCount int
Lock sync.Mutex
Duration time.Duration
Id int
}
func (rc *Server) WindowAvailable() bool{
rc.Lock.Lock()
defer rc.Lock.Unlock()
return rc.CurrentCount <= rc.MaxCount
}
func (rc *Server) Decrease(){
rc.Lock.Lock()
defer rc.Lock.Unlock()
rc.CurrentCount -= 1
}
func (rc *Server) Increase(){
rc.Lock.Lock()
defer rc.Lock.Unlock()
rc.CurrentCount +=1
}
defer妙用示例
- 记录函数运行时间
package main
import (
"fmt"
"time"
)
func TimeProfile() func(){
start := time.Now()
return func() {
fmt.Printf("time duration:%s",time.Since(start))
}
}
func work() {
time.Sleep(time.Second)
}
func main() {
defer TimeProfile()()
work() //此处可以放置你想测运行时间的函数
}
//输出
//time duration:1.004610669s