一、error与panic:
- error:可预见的错误
- panic:不可预见的错误,panic一般通过defer中的recover()捕获
对于有风险的代码,若发生panic则会导致程序异常退出,例如数组越界。
1. panic的操守
- panic是有秩序的,退出之前会执行完先处理完当前goroutine已经defer【挂上去】的任务,若某个defer在panic之后,则不会被执行。
- panic仅保证当前goroutine下的defer都会被调到,但不保证其他协程的defer也会调到
- 同一goroutine下的已经挂上去的多个defer,回溯执行,即先进后出
例如:
func main() {
defer fmt.Println("defer main")
var user = os.Getenv("USER_")
go func() {
defer fmt.Println("defer caller")
func() {
defer func() {
fmt.Println("defer here")
}()
if user == "" {
panic("should set user env.")
}
}()
}()
time.Sleep(1 * time.Second)
fmt.Printf("get result")
}
//result:
defer here
defer caller
panic: should set user env.
func main() {
defer fmt.Println("defer main")
var user = os.Getenv("USER_")
go func() {
defer fmt.Println("defer caller")
func() {
if user == "" {
panic("should set user env.")
}
defer func() {//panic发生时,尚未挂上去,所以不执行
fmt.Println("defer here")
}()
}()
}()
time.Sleep(1 * time.Second)
fmt.Printf("get result")
}
//result:
defer caller
panic: should set user env.
2. panic的处理:defer+recover
- defer中通过recover截取panic,达到类似try_catch的效果。
- defer执行在return前或者panic后
defer func() {
err := recover()
if err != nil {//recover若捕获错误,则为panic了
var errMsg, logMsg string
switch err.(type) {
case *global.FlyWebError: //web error
webErr := err.(*global.FlyWebError)
code = webErr.Code
errMsg = util.GetErrMsgByCode(code)
logMsg = webErr.Msg
default: //未知错误
code = util.ERRCODE_UNKNOWN
errMsg = util.GetErrMsgByCode(code)
logMsg = fmt.Sprint(err)
}
log.Error(common.LOG_CMD, logMsg, 0, 0, 0)
}
}()
二、go func()与panic
go func()为开启一个子协程执行该匿名函数内的逻辑。
- 但是主进程中的defer无法捕获子协程中的panic异常,所以go func()中一般有defer()处理该子协程内发生的panic.
go func()+defer一般用在:多次远程调用,每一次新开一个协程去执行,无需等待结果即可循环下一次,例如:
wg := sync.WaitGroup{}
wg.Add(len(arrCommentIds))
for i := 0; i < len(arrCommentIds); i++ {
go func(commentId []string) {
defer func() {
wg.Done()
if err0 := recover(); err0 != nil {
err = errors.New(fmt.Sprint(err0))
}
}()
var tempCommentList *centerpb.CommentList
tempCommentList, err = rpc.GetCommentInfo(commentId, targetId, filter)
if err != nil {
return
}
mutex.Lock()
commentList.Comments = append(commentList.Comments, tempCommentList.Comments...)
mutex.Unlock()
}(arrCommentIds[i])
}
wg.Wait()//用于等待协程执行完毕
所以defer的用途一般是:
- 捕获异常
- 封装一些必须要执行的逻辑,顺序执行情况下担心前面代码return了而忘记/走不到该行逻辑。比如锁的释放。
使用defer是有代价的,会造成cpu的浪费,要合理使用。