首頁  >  文章  >  後端開發  >  Go 語言中的 defer 與 panic 的關係是什麼?

Go 語言中的 defer 與 panic 的關係是什麼?

王林
王林原創
2023-06-11 09:00:08891瀏覽

Go 語言是一種新式的程式語言,它新增了一些特色的語法和特性,其中 defer 和 panic 是其中兩個非常重要的特性。本文將會介紹 Go 語言中 defer 和 panic 的關係以及它們的用法和特徵。

defer 的用法

Go 語言中的 defer 語句用來註冊一個函數,當這個函數執行結束或目前作用域結束時,會自動執行這個被註冊的函數。 defer 可以用於釋放資源、鎖的解鎖和錯誤處理等多個場景。

下面是一個 defer 釋放資源的範例:

func main() {
    file, err := os.Open("myfile.txt")
  // 在函数结束时,会自动关闭文件
    defer file.Close()
  
    if err != nil {
        fmt.Println("Failed to open file.")
        return
    }
    // ...
}

透過 defer 檔案的 Close() 函數被註冊,當函數執行結束時會自動關閉檔案。

下面是一個 defer 鎖定的解鎖的範例:

func main() {
    var lock sync.Mutex
    lock.Lock()
  // 当函数执行结束时,会自动解锁
    defer lock.Unlock()
  
    // ...
}

當函數執行結束時會自動呼叫 Unlock() 函數解鎖鎖定。

defer 的執行順序是從後往前,這表示如果多個 defer 語句被註冊,它們會按照相反的順序執行。下面範例會輸出Defer 2,然後輸出Defer 1

func main() {
    defer fmt.Println("Defer 1")
    defer fmt.Println("Defer 2")
    fmt.Println("Hello")
}

panic 的用法

Go 語言中的 panic 關鍵字用於拋出一個異常,並終止目前函數或程式的執行。 panic 會沿著函數呼叫堆疊向上傳遞,直到被 recover() 函數捕捉到為止。如果沒有被捕捉到,整個程式會被退出,並輸出一個呼叫堆疊。

在下面範例程式碼中,當輸入的字串長度小於 5 時,會觸發 panic,終止程式的執行。

func hello(name string) {
    if len(name) < 5 {
        panic("Name is too short.")
    }
    fmt.Println("Hello", name)
}

func main() {
    hello("Tom")
    hello("Bob")
    hello("me")
}

輸出結果如下:

Hello Tom
Hello Bob
panic: Name is too short.

goroutine 1 [running]:
main.hello(...)
    /Users/user/goland/src/main.go:4
main.main()
    /Users/user/goland/src/main.go:10 +0x81
exit status 2

這裡我們可以看到當輸入的 name 為me時,會觸發 panic 並終止程式執行。

defer 和 panic 的關係

panic 起到立即終止程式執行的作用,這意味著它可以在任何時候觸發,包括在函數執行結束之前。為了確保程式能夠及時釋放資源並執行一些必要的清理工作,Go 語言引入了 defer 這個機制,使得函數在退出之前能夠先執行一些清理操作。

當一個函數中觸發了 panic,它會立即退出,並執行當前函數之前註冊的所有 defer 函數。下面這個範例程式碼手動觸發了一個 panic,並在退出前執行了兩次 defer 函數。

func main() {
    defer fmt.Println("Defer 1")
    defer fmt.Println("Defer 2")
    
    panic("Oops! Something went wrong.")
}

輸出結果如下:

Defer 2
Defer 1
panic: Oops! Something went wrong.

goroutine 1 [running]:
main.main()
    /Users/user/goland/src/main.go:7 +0x81
exit status 2

我們可以看到在觸發 panic 之後,兩個 defer 函數被逆序執行。

除了可以在函數的最後註冊 defer 函數,Go 語言還允許在函數中註冊多次 defer 函數。這意味著如果一個函數中有多個 defer 函數,即使其中一個 defer 觸發了 panic,也可以保證其它的 defer 函數仍然能夠執行。

下面這個範例程式碼示範了當函數註冊了多個 defer 語句時,其中一個 defer 函數觸發了 panic,但其它的 defer 函數仍然執行的情況。

func init() {
    fmt.Println("Init 1")
}

func init() {
    fmt.Println("Init 2")
}

func main() {
    defer fmt.Println("Defer 1")
    
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("Recovered:", err)
        }
    }()
    
    defer fmt.Println("Defer 2")
    
    panic("Oops! Something went wrong.")
}

輸出結果如下:

Init 1
Init 2
Defer 2
Recovered: Oops! Something went wrong.
Defer 1

我們可以看到,函數先執行了兩個 init 函數,然後依序執行了三個 defer 函數。當其中一個 defer 觸發了 panic,但被另一個 defer 捕捉到並恢復了程序,最終兩個 defer 函數都正常執行。

在實際開發中,defer 和 panic 往往是成對使用的,defer 用於釋放資源和執行清理操作,panic 用於處理異常情況。當一個函數需要在退出之前執行多個清理作業時,我們可以在函數開頭使用一個 defer 包裝函數,並使用 recover() 函數避免函數提前退出。這種寫法非常常見,也為我們寫健壯的程式提供了有力的保證。

以上是Go 語言中的 defer 與 panic 的關係是什麼?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn