1. 程式人生 > 其它 >golang中defer,panic,recover的用法

golang中defer,panic,recover的用法

在golang當中,defer程式碼塊會在函式呼叫連結串列中增加一個函式呼叫。這個函式呼叫不是普通的函式呼叫,而是會在函式正常返回,也就是return之後新增一個函式呼叫。因此,defer通常用來釋放函式內部變數。 通過defer,我們可以在程式碼中優雅的關閉/清理程式碼中所使用的變數。defer作為golang清理變數的特性,有其獨有且明確的行為。

defer特性:在函式返回之前,呼叫defer函式的操作,簡化函式的清理工作。

1、當defer被宣告時,其引數就會被實時解析

func a() {
    i := 0
    defer fmt.Println(i) // 輸出的是0,因為i=0,已經明確告訴golang在程式退出時執行輸出0的操作
i++ defer fmt.Println(i) // 輸出的是1 return }

輸出結果是 1 0

2、函式內有多個defer函式,遵循後進先出原則

func b() {
    for i := 0; i < 4; i++ {
        defer fmt.Print(i)
    }
    // 輸出結果是 3210,即LIFO。
}

func c() int {
    var i int
 
    defer func() {
        i++
        fmt.Println("a defer2:", i) // 輸出結果為 a defer2: 2
}() defer func() { i++ fmt.Println("a defer1:", i) // 輸出結果為 a defer1: 1 }() return i }

同個函式的defer遵循後進先出

3、defer函式返回值賦值

func d() (result int) {
    defer func() {
        result++
    }()
    return 0
    // 返回值是1,在defer中被更改了
}

func e() (r int) {
    t := 5
    r = t
    defer func() {
        t 
= t + 5 }() return t // 返回值是5,在defer中並沒有修改r的值 } func f() (r int) { defer func(r int) { r = r + 5 }(r) return 1 } func g() (r int) { defer func(r *int) { *r = *r + 5 }(&r) return 1 // 返回值是6,defer的傳入引數是引用型別,取地址操作會改變最終r的值 }

函式的返回值是有可能在defer函式中被更改,本質是return 語句並不是原子指令。

二、panic用法

Panic is a built-in function that stops the ordinary flow of control and begins panicking. When the function F calls panic, execution of F stops, any deferred functions in F are executed normally, and then F returns to its caller. To the caller, F then behaves like a call to panic. The process continues up the stack until all functions in the current goroutine have returned, at which point the program crashes. Panics can be initiated by invoking panic directly. They can also be caused by runtime errors, such as out-of-bounds array accesses.

panic是內建函式,會中斷函式F的正常執行流程,從F函式中跳出來,跳回到F函式的呼叫者。對於呼叫者來說, F看起來就是一個panic,所以呼叫者會繼續向上跳出,直到當前goroutine返回。在跳出的過程中,程序會保持這個函式棧。當goroutine退出時,程式會crash。

除了主動呼叫panic之外,任何執行時錯誤都會造成panic。

func TestPanic(t *testing.T) {
    panicTest()
}

func panicTest() {
    defer func() { fmt.Println(1) }()
    defer func() { fmt.Println(2) }()
    panic("手動觸發異常")
    fmt.Println("觸發異常,將無法執行")
}
執行結果如下:
=== RUN   TestPanic
2
1
--- FAIL: TestPanic (0.00s)
panic: 手動觸發異常 [recovered]
    panic: 手動觸發異常

goroutine 6 [running]:
testing.tRunner.func1.1(0x6ede00, 0x741540)
    E:/Program Files (x86)/go/src/testing/testing.go:1072 +0x310
testing.tRunner.func1(0xc00002b080)

在panic觸發異常之前,defer函式還是按順序輸出了,但後續的內容就無法執行了。

三、recover用法

Recover is a built-in function that regains control of a panicking goroutine. Recover is only useful inside deferred functions. During normal execution, a call to recover will return nil and have no other effect. If the current goroutine is panicking, a call to recover will capture the value given to panic and resume normal execution.

recover也是一個內建函式,用於捕捉程式異常,必須與defer函式配合使用,通常做法是recover返回非nil時,出現錯誤,用於執行程式清理資源操作。

func TestRecover(t *testing.T) {
    recoverTest()
}

func recoverTest() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("recover捕獲到panic")
            fmt.Println(err)
        }
    }()

    fmt.Println("recoverTest執行開始")
    panic("執行出現異常")
}
--執行結果
=== RUN   TestRecover
recoverTest執行開始
recover捕獲到panic
執行出現異常
--- PASS: TestRecover (0.00s)
PASS

程序 已完成,退出程式碼為 0