defer、panic | 青训营

56 阅读3分钟

#青训营笔记创作活动

使用 defer、panic 和 recover 函数进行控制

现在,让我们看看 Go 特有的一些控制流:deferpanic 和 recover。 其中的每个函数都有几个用例。 我们将在这里探讨最重要的用例。

defer 函数

在 Go 中,defer 语句会推迟函数(包括任何参数)的运行,直到包含 defer 语句的函数完成。 通常情况下,当你想要避免忘记任务(例如关闭文件或运行清理进程)时,可以推迟某个函数的运行。

可以根据需要推迟任意多个函数。 defer 语句按逆序运行,先运行最后一个,最后运行第一个。

通过运行以下示例代码来查看此模式的工作原理:

package main
import "fmt"
func main() { 
    for i := 1; i <= 4; i++ { 
        defer fmt.Println("deferred", -i)
        fmt.Println("regular", i) 
    } 
}

下面是代码输出:

regular 1 
regular 2 
regular 3 
regular 4 
deferred -4 
deferred -3 
deferred -2 
deferred -1

在此示例中,请注意,每次推迟 fmt.Println("deferred", -i) 时,都会存储 i 的值,并且函数调用将添加到队列中。 在 main() 函数输出完 regular 值后,所有推迟的调用都会运行。 请注意,延迟调用输出的顺序相反(后进先出),因为它们是从队列中弹出的。

defer 函数的一个典型用例是在使用完文件后将其关闭。 下面是一个示例:

package main
import (
    "io"
    "os"
    "fmt"
)

func main() {
    newfile, error := os.Create("learnGo.txt")
    if error != nil {
        fmt.Println("Error: Could not create file.")
        return
    }
    defer newfile.Close()

    if _, error = io.WriteString(newfile, "Learning Go!"); error != nil {
	    fmt.Println("Error: Could not write to file.")
        return
    }

    newfile.Sync()
}

创建或打开某个文件后,可以推迟 .Close() 函数的执行,以免在你完成后忘记关闭该文件。

panic 函数

运行时错误会使 Go 程序崩溃,例如尝试通过使用超出范围的索引或取消引用 nil 指针来访问数组。 你也可以强制程序崩溃。

内置 panic() 函数可以停止 Go 程序中的正常控制流。 当你使用 panic 调用时,任何延迟的函数调用都将正常运行。 进程会在堆栈中继续,直到所有函数都返回。 然后,程序会崩溃并记录日志消息。 此消息包含错误信息和堆栈跟踪,有助于诊断问题的根本原因。

调用 panic() 函数时,可以添加任何值作为参数。 通常,你会发送一条错误消息,说明为什么会进入紧急状态。

例如,下面的代码将 panic 和 defer 函数组合在一起。 尝试运行此代码以了解控制流的中断。 请注意,清理过程仍会运行。

package main

import "fmt"

func highlow(high int, low int) {
    if high < low {
        fmt.Println("Panic!")
        panic("highlow() low greater than high")
    }
    defer fmt.Println("Deferred: highlow(", high, ",", low, ")")
    fmt.Println("Call: highlow(", high, ",", low, ")")

    highlow(high, low + 1)
}

func main() {
    highlow(2, 0)
    fmt.Println("Program finished successfully!")
}

输出如下:

Call: highlow( 2 , 0 )
Call: highlow( 2 , 1 )
Call: highlow( 2 , 2 )
Panic!
Deferred: highlow( 2 , 2 )
Deferred: highlow( 2 , 1 )
Deferred: highlow( 2 , 0 )
panic: highlow() low greater than high

goroutine 1 [running]:
main.highlow(0x2, 0x3)
	/tmp/sandbox/prog.go:13 +0x34c
main.highlow(0x2, 0x2)
	/tmp/sandbox/prog.go:18 +0x298
main.highlow(0x2, 0x1)
	/tmp/sandbox/prog.go:18 +0x298
main.highlow(0x2, 0x0)
	/tmp/sandbox/prog.go:18 +0x298
main.main()
	/tmp/sandbox/prog.go:6 +0x37

Program exited: status 2.

下面是运行代码时会发生的情况:

  1. 一切正常运行。 程序将输出传递到 highlow() 函数中的高值和低值。
  2. 如果 low 的值大于 high 的值,则程序会崩溃。 会显示“Panic!”消息。 此时,控制流中断,所有推迟的函数都开始输出“Deferred...”消息。
  3. 程序崩溃,并显示完整的堆栈跟踪。 不会显示“Program finished successfully!”消息。

在发生未预料到的严重错误时,系统通常会运行对 panic() 函数的调用。 若要避免程序崩溃,可以使用名为 recover() 的另一个函数。