1. 程式人生 > >Go語言開發(七)、Go語言錯誤處理

Go語言開發(七)、Go語言錯誤處理

pro package error 先進後出 goroutin cee 錯誤處理機制 避免 而是

Go語言開發(七)、Go語言錯誤處理

一、defer延遲函數

1、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)
}

func main() {
   deferTest()//3,2,1
}

2、defer延遲函數應用

A、簡化資源回收

mu.Lock() 
defer mu.Unlock()

defer?有一定的開銷, 為了節省性能可以避免使用的defer?
B、捕獲panic異常
Go語言中,panic用於拋出異常,,recover用於捕獲異常。
recover只能在defer語句中使用,直接調用recover是無效的。

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?後修改函數的返回值。

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()被調用。

二、錯誤處理

1、錯誤處理簡介

Go語言通過內置的錯誤接口提供了簡單的錯誤處理機制。
error類型是一個接口類型,定義如下:

type error interface {
    Error() string}

Golang中引入error接口類型作為錯誤處理的標準模式,如果函數要返回錯誤,則返回值類型列表中肯定包含error。

2、錯誤處理使用

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)
   }
}

三、異常處理

1、異常處理簡介

Go使用panic()函數拋出異常,在defer語句中調用recover()函數捕獲異常。

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所屬的進程打印異常信息後直接退出。

2、異常處理使用示例

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)
}

Go語言開發(七)、Go語言錯誤處理