GO panic和recover的用法

来源:互联网 发布:plc通信 十六进制数据 编辑:程序博客网 时间:2024/06/08 05:39

golang中没有try ... catch...这类异常捕获语句,但是提供了panic和recover内建函数,用于抛出异常以及异常的捕获。

func panic

func panic(v interface{})

The panic built-in function stops normal execution of the current goroutine. When a function F calls panic, normal execution of F stops immediately. Any functions whose execution was deferred by F are run in the usual way, and then F returns to its caller. To the caller G, the invocation of F then behaves like a call to panic, terminating G's execution and running any deferred functions. This continues until all functions in the executing goroutine have stopped, in reverse order. At that point, the program is terminated and the error condition is reported, including the value of the argument to panic. This termination sequence is called panicking and can be controlled by the built-in function recover.

func recover

func recover() interface{}

The recover built-in function allows a program to manage behavior of a panicking goroutine. Executing a call to recover inside a deferred function (but not any function called by it) stops the panicking sequence by restoring normal execution and retrieves the error value passed to the call of panic. If recover is called outside the deferred function it will not stop a panicking sequence. In this case, or when the goroutine is not panicking, or if the argument supplied to panic was nil, recover returns nil. Thus the return value from recover reports whether the goroutine is panicking.


1、Panic

The usual way to report an error to a caller is to return an error as an extra return value. The canonical Read method is a well-known instance; it returns a byte count and an error. But what if the error is unrecoverable? Sometimes the program simply cannot continue.


For this purpose, there is a built-in function panic that in effect creates a run-time error that will stop the program. The function takes a single argument of arbitrary type—often a string—to be printed as the program dies. It's also a way to indicate that something impossible has happened, such as exiting an infinite loop.


上面两段话的主要意思是,我们通常使用error返回值来给调用者返回错误,但是,如果程序出现了致命的错误,导致整个程序无法进行下去,golang提供了panic函数,用来实现程序的退出。

e.g.

package main
import "os"
func main() {
    var user = os.Getenv("USER")
    if user == "" {
        panic("The USER environment variable is not set.")
    }
}
运行结果:

C:/go/bin/go.exe run test.go [E:/project/go/test/src]

panic: The USER environment variable is not set.


goroutine 1 [running]:

panic(0x467d20, 0xc04200a2d0)

c:/go/src/runtime/panic.go:500 +0x1af

main.main()

E:/project/go/test/src/test.go:8 +0xa5

exit status 2

错误: 进程退出代码 1.

2、Recover

When panic is called, including implicitly for run-time errors such as indexing a slice out of bounds or failing a type assertion, it immediately stops execution of the current function and begins unwinding the stack of the goroutine, running any deferred functions along the way. If that unwinding reaches the top of the goroutine's stack, the program dies. However, it is possible to use the built-in function recover to regain control of the goroutine and resume normal execution.


A call to recover stops the unwinding and returns the argument passed to panic. Because the only code that runs while unwinding is inside deferred functions, recover is only useful inside deferred functions.


One application of recover is to shut down a failing goroutine inside a server without killing the other executing goroutines.


recover always returns nil unless called directly from a deferred function。


上面几段话的大体意思是当显式或隐式的调用panic,会立马停止当前函数的执行,然后开始当前goroutinue的栈展开,如果有defer函数,会一并被执行。通过在defer函数里调用recover,可以使程序恢复正常的执行。recover函数只有在defer函数里有效,其他地方都返回nil。


e.g.

//下面的代码通过panic和recover的配合使用,增加了程序的健壮性。单个goroutine的失败,不影响整个程序的运行。

//main.go

package main
import "fmt"
import "time"
func serve(ch <-chan int) {//分发函数
    for val := range ch {
        go handle(val)
    }
}
func handle(x int) {//处理函数
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("work failed:", err)
        }
    }()
    if x == 3 {
        panic(x)
    }
    fmt.Println("work succeeded:", x)
}
func main() {
    var ch = make(chan int, 6)
    for i := 1; i <= 6; i++ {
        ch <- i
    }
    close(ch)
    go serve(ch)
    time.Sleep(time.Millisecond * 100)
}
运行结果:

C:/go/bin/go.exe run test2.go [E:/project/go/test/src]

work succeeded: 6

work succeeded: 1

work succeeded: 2

work failed: 3

work succeeded: 4

work succeeded: 5

成功: 进程退出代码 0.

e.g.

//该案例通过在f2函数中调用panic,导致程序直接崩溃,无法进行下去。

//main.go

package main
import "fmt"
func f1() {
    fmt.Println("This is f1()")
}
func f2() {
    fmt.Println("This is f2()")
    panic(1)
}
func f3() {
    fmt.Println("This is f3()")
}
func main() {
    f1()
    f2()
    f3()
}
运行结果:

C:/go/bin/go.exe run test4.go [E:/project/go/test/src]

This is f1()

This is f2()

panic: 1

goroutine 1 [running]:

panic(0x494e80, 0xc04200a330)

c:/go/src/runtime/panic.go:500 +0x1af

main.f2()

E:/project/go/test/src/test4.go:11 +0xea

main.main()

E:/project/go/test/src/test4.go:20 +0x20

exit status 2

错误: 进程退出代码 1.

e.g.

//通过在f2的defer函数中调用recover,捕获了异常,程序恢复正常的执行。

//main.go

package main
import "fmt"
func f1() {
    fmt.Println("This is f1()")
}
func f2() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("Exception has been caught.")
        }
    }()
    fmt.Println("This is f2()")
    panic(1)
}
func f3() {
    fmt.Println("This is f3()")
}
func main() {
    f1()
    f2()
    f3()
}
运行结果:

C:/go/bin/go.exe run test4.go [E:/project/go/test/src]

This is f1()

This is f2()

Exception has been caught.

This is f3()

成功: 进程退出代码 0.




0 0
原创粉丝点击