关于golang:聊聊golang的panic与recover

68次阅读

共计 2577 个字符,预计需要花费 7 分钟才能阅读完成。

本文次要钻研一下 golang 的 panic 与 recover

panic 与 recover

recover 在如下三种状况下返回 nil

  • panic 参数为 nil
  • goroutine 没有产生 panic
  • recover 不是在 defer func 中调用

实例

实例 1

var fc func() string

func protect(g func() string) {defer func() {fmt.Println("done") // Println executes normally even if there is a panic
        if x := recover(); x != nil {fmt.Printf("run time panic: %v", x)
        }
    }()
    fmt.Println("start")
    fmt.Println(g())
}
func main() {protect(fc)
}

这里 fc 为 nil,因此产生 runtime panic,而后被 defer 的 recover 捕捉

实例 2

func deferRecover() {defer func() {fmt.Println("defer1: recover")
        if err := recover(); err != nil {fmt.Println(err)
        } else {fmt.Println("defer1 recover nil")
        }
    }()

    defer func() {fmt.Println("defer2: recover")
        if err := recover(); err != nil {fmt.Println(err)
        }
    }()

    panic("manual panic")

    defer func() {fmt.Println("never be executed") 
    }()}

这里 defer2 捕捉了 panic,defer1 就捕捉不到 panic 了

实例 3

func paincInDefer() {defer func() {fmt.Println("defer1 recover")
        if err := recover(); err != nil {
            // main panic is override by defer2 panic
            fmt.Println(err)
        } else {fmt.Println("defer1 recover nil")
        }
    }()

    defer func() {fmt.Println("defer2 begin to panic")
        panic("defer2 panic")
    }()

    panic("main panic")
}

这里 defer2 产生了 panic,” 笼罩 ” 了原始的 panic,defer1 捕捉的是最初一个 panic 即 defer2 产生的 panic

实例 4

func paincInDeferNotRecover() {defer func() {fmt.Println("defer1")
    }()

    defer func() {fmt.Println("defer2 begin to panic")
        panic("defer2 panic")
    }()

    panic("main panic")
}

这里 defer2 产生了 panic,defer1 没有捕捉,产生的 panic 顺次显示了 main panic 及 defer2 panic

输入

defer2 begin to panic
defer1
panic: main panic
        panic: defer2 panic

goroutine 1 [running]:
main.paincInDeferNotRecover.func2()

实例 5

func multiPaincInDefer() {defer func() {fmt.Println("defer1")
        panic("defer3 panic")
    }()

    defer func() {fmt.Println("defer2 begin to panic")
        panic("defer2 panic")
    }()

    panic("main panic")
}

输入

defer2 begin to panic
defer1
panic: main panic
        panic: defer2 panic
        panic: defer3 panic

goroutine 1 [running]:
main.multiPaincInDefer.func1()

实例 6

func panicAfterRecover() {defer func() {fmt.Println("defer1")
        if err := recover(); err != nil {panic("defer1 panic after recover")
        } else {fmt.Println("defer1 recover nil")
        }
    }()

    defer func() {fmt.Println("defer2 begin to panic")
        panic("defer2 panic")
    }()

    panic("main panic")
}

输入

defer2 begin to panic
defer1
panic: main panic
        panic: defer2 panic [recovered]
        panic: defer1 panic after recover

goroutine 1 [running]:
main.panicAfterRecover.func1()
        /defer.go:180 +0x10d
panic(0x10b2020, 0x10eaed0)
        /usr/local/go/src/runtime/panic.go:969 +0x1b9
main.panicAfterRecover.func2()
        /defer.go:188 +0x95
panic(0x10b2020, 0x10eaee0)
        /usr/local/go/src/runtime/panic.go:969 +0x1b9
main.panicAfterRecover()
        /defer.go:191 +0x68
main.main()
        /defer.go:215 +0x25
exit status 2

小结

  • recover 必须在 defer 中调用才能够,多个 defer 呈现了 panic 没有 recover,则 panic 信息会列出所有的 painc 信息
  • 如果 defer 也呈现了 panic 则若有被 recover 则 recover 的是最初一个 panic,非 defer 的 panic 被 ” 笼罩 ”
  • 如果 defer 的 recover 之后呈现了 panic,则最初 panic 会呈现被 recover 的那个 panic 信息

doc

  • Handling_panics

正文完
 0