golang爬坑筆記之自問自答系列(8)——關於defer

介紹

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章