Defer,Panic,Recover简单应用

Defer

概述
defer语句将函数调用推送到列表上。 周围函数返回后,将执行已保存的呼叫列表。 Defer通常用于简化执行各种清理操作的功能。
例子:

func CopyFile(dstName, srcName string) (written int64, err error) {
    src, err := os.Open(srcName)
    if err != nil {
        return
    }

    dst, err := os.Create(dstName)
    if err != nil {
        return
    }

    written, err = io.Copy(dst, src)
    dst.Close()
    src.Close()
    return
}

这可行,但是有一个错误。 如果对os.Create的调用失败,该函数将返回而不关闭源文件。 通过在第二个return语句之前调用src.Close可以很容易地解决此问题,但是如果函数更复杂,则问题可能不会那么容易被发现和解决。 通过引入defer语句,我们可以确保始终关闭文件:

func CopyFile(dstName, srcName string) (written int64, err error) {
    src, err := os.Open(srcName)
    if err != nil {
        return
    }
    defer src.Close()

    dst, err := os.Create(dstName)
    if err != nil {
        return
    }
    defer dst.Close()

    return io.Copy(dst, src)
}

Defer语句使我们可以考虑在打开每个文件后立即关闭它们,从而确保无论函数中有return语句多少,文件都将被关闭。
关于defer的三个规则:

  1. defer语句执行的参数是执行defer语句时参数的值
func a() {
    i := 0
    defer fmt.Println(i)
    i++
    return
}

运行a()函数得到的输出是0,因为defer此语句i的值就是0

  1. 周围的函数返回后,将按照后进先出的顺序执行延迟的函数调用。
func b() {
    for i := 0; i < 4; i++ {
        defer fmt.Print(i)
    }
}

输出3210

  1. defer参数可以读取并修改函数的返回值
func c() (i int) {
    defer func() { i++ }()
    return 1
}

输出值为2

Panic

概述
go语言的内置函数,用于终止正常的控制流程并抛出恐慌。
当函数F调用panic,会执行该函数所有已保存在呼叫队列的defer函数,然后返回其调用方,对于调用方而言F表现得像是发生了恐慌,该过程将继续执行对战,知道返回当前goroutine中的所有函数,此时程序崩溃。可以直接调用panic来生成恐慌,他们也可以是由运行时的错误引起的,例如越界数组访问。

Recover

go语言的内置函数,可以重新活成发生panic的gorouting的控制权。
recover仅仅在defer函数中使用,在正常执行期间,回复调用将返回nil并没有其他效果。若goroutine处于panic状态,改调用会捕获发送给panic的值并回复goroutine的正常执行。

example

func main() {
    f()
    fmt.Println("Returned normally from f.")
}

func f() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in f", r)
        }
    }()
    fmt.Println("Calling g.")
    g(0)
    fmt.Println("Returned normally from g.")
}

func g(i int) {
    if i > 3 {
        fmt.Println("Panicking!")
        panic(fmt.Sprintf("%v", i))
    }
    defer fmt.Println("Defer in g", i)
    fmt.Println("Printing in g", i)
    g(i + 1)
}

输出:

Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
Recovered in f 4
Returned normally from f.

若去除掉f中的recover函数

func f() {
	defer func() {
	//	if r := recover(); r != nil {
	//		fmt.Println("Recovered in f", r)
	//	}
	fmt.Println("do nothing")
	}()
	fmt.Println("Calling g.")
	g(0)
	fmt.Println("Returned normally from g.")
}

则并不会输出Returned normally from f.

Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
do nothing
panic: 4

goroutine 1 [running]:
.....

原文:
https://blog.golang.org/defer-panic-and-recover

发布了212 篇原创文章 · 获赞 33 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/hello_bravo_/article/details/104443938