defer在聲明時不會當即執行,而是在defer所在的函數return後,再按照FILO(先進後出)的原則依次執行每個defer,通常用於異常處理、釋放資源、清理數據、記錄日誌等。
每次defer語句執行時,defer修飾函數的返回值和參數取值會照常進行計算和保存,可是defer修飾的函數不會執行。等到上一級函數返回前,會按照defer的聲明順序倒序執行所有defer的函數。defer所修飾函數的任何返回值都會被丟棄。
若是一個defer所修飾函數的值爲nil,則defer的函數會在函數執行時panic(異常),而不會在defer語句執行時panic。defer所修飾函數的上一級函數即便拋出異常,defer所修飾函數也會被執行的,確保資源被合法釋放。
defer延遲函數使用示例以下:數組
package main import "fmt" func deferTest() { defer fmt.Println(1) defer fmt.Println(2) fmt.Println(3) for i := 100; i < 105; i++ { defer fmt.Println(i) //執行defer時進行參數計算 } } func main() { deferTest() } // output: 3 104 103 102 101 100 2 1
A、簡化資源回收安全
mu.Lock() defer mu.Unlock()
defer 有必定的開銷, 爲了節省性能能夠避免使用的defer
B、捕獲panic異常
Go語言中,panic用於拋出異常,,recover用於捕獲異常。
recover只能在defer語句中使用,直接調用recover是無效的。app
package main import "fmt" func deferRecover(){ defer func () { if r := recover(); r != nil { fmt.Println("recover") } }() fmt.Println("exception will be happen") panic("exception has happped.") fmt.Println("return normally") } func main() { deferRecover() }
C、修改返回值
defer能夠用於在 return 後修改函數的返回值。ide
package main import "fmt" func deferReturn(a,b int)(sum int){ defer func(){ sum += 100 }() sum = a + b return sum } func main() { sum := deferReturn(1,6) fmt.Println(sum)//107 }
D、安全回收資源函數
func set(mu *sync.Mutex, arr []int, i, v int) { mu.Lock() defer mu.Unlock() arr[i] = v }
若是運行時拋出切片越界異常,能夠保證mu.Unlock()被調用。性能
Go語言經過內置的錯誤接口提供了簡單的錯誤處理機制。
error類型是一個接口類型,定義以下:日誌
type error interface { Error() string}
Golang中引入error接口類型做爲錯誤處理的標準模式,若是函數要返回錯誤,則返回值類型列表中確定包含error。code
package main import ( "fmt" "errors" ) //定義一個DivideError類型 type DivideError struct { dividee int divider int } //實現error接口 func (err *DivideError) Error() error{ strFormat := `Cannot proceed, the divider is zero. dividee: %d divider: 0` return errors.New(fmt.Sprintf(strFormat, err.dividee)) } //定義除法運算 func divide(vardividee int, vardivider int)(result int, errmsg error){ if vardivider == 0{ divideErr := DivideError{ dividee:vardividee, divider:vardivider, } errmsg = divideErr.Error() return 0,errmsg }else{ return vardividee/vardivider,nil } } func main() { //正常狀況 if result, err := divide(100, 10); err != nil{ fmt.Println(err) }else{ fmt.Println("100/10 = ", result) } //當被除數爲零的時候會返回錯誤信息 if _, errorMsg := divide(100, 0); errorMsg != nil{ fmt.Println(errorMsg) } }
Go使用panic()函數拋出異常,在defer語句中調用recover()函數捕獲異常。orm
func panic(interface{})//接受任意類型參數 無返回值 func recover() interface{}//能夠返回任意類型 無參數
panic()是一個內置函數,能夠中斷原有的控制流程,進入一個panic流程中。當函數F調用panic,函數F的執行被中斷,但F中的延遲函數(必須是在panic前的已加載的defer)會正常執行,而後F函數逐層向上返回,直到發生panic的goroutine中全部調用的函數返回,此時程序退出。異常能夠直接調用panic產生,也能夠由運行時錯誤產生,例如訪問越界的數組。
recover()是一個內置函數,可讓進入panic流程中的goroutine恢復過來。recover僅在延遲函數中有效。在正常的執行過程當中,調用recover會返回nil,而且沒有其它任何效果。若是當前的goroutine陷入panic,調用recover能夠捕獲到panic的輸入值,而且恢復正常的執行。
通常狀況下,recover()應該在一個使用defer關鍵字的函數中執行以有效截取錯誤處理流程。若是沒有在發生異常的goroutine中明確調用恢復過程(使用recover關鍵字),會致使goroutine所屬的進程打印異常信息後直接退出。接口
package main import ( "errors" "fmt" ) //定義一個DivideError類型 type DivideError struct { dividee int divider int } //實現error接口 func (err *DivideError) Error() error{ strFormat := `Cannot proceed, the divider is zero. dividee: %d divider: 0` return errors.New(fmt.Sprintf(strFormat, err.dividee)) } //定義除法運算 func divide(dividee int, divider int)(result int){ defer func() { if r := recover();r != nil{ divideErr := DivideError{ dividee:dividee, divider:divider, } fmt.Println(divideErr.Error()) } }() result = dividee/divider return result } func main() { a := divide(100,0) fmt.Println(a) }