介紹
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