Go语言的panic类型


Go语言的panic类型

2019-03-06 13:05:45 Elonjelinekopen in new window 阅读数 225更多

分类专栏: go语言open in new window 后端open in new window

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SAopen in new window 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/Charliewolf/article/details/88230502open in new window

1、数组越界(运行时错误)。

package main
import "fmt"
func main() {
    var s []string
    fmt.Println(s)
    fmt.Println(s[0])
}

运行结果:

[] panic: runtime error: index out of range goroutine 1 [running]: main.main()

index out of range:索引超出范围(数组越界)

2、空指针异常(运行时错误)。

package main
import "fmt"
type Person struct {
    Name string
    Age  int
}
func main() {
    var p *Person
    fmt.Println(p)
    fmt.Println(p.Name)
}

运行结果:

panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x10914c8] goroutine 1 [running]: main.main()

segmentation violation:内存段异常。

3、类型断言失败(接口转换异常)。

package main import "fmt" func main() { add(20, 18) add(1, "hello") } func add(a, b interface{}) { i := a.(int) j := b.(int) fmt.Println(i+j) }

运行结果:

panic: interface conversion: interface {} is string, not int 38 goroutine 1 [running]: main.add(0x10a0f60, 0x10d6840, 0x10a1660, 0x10d6b90)

interface conversion: interface {} is string, not int:接口转换,接口是string,不是int。

4、通道为空,通道已关闭

package main func main() { var ch chan int close(ch) }

运行结果:

panic: close of nil channel goroutine 1 [running]: main.main()

close of nil channel关闭空的通道。

package main func main() { var ch chan int ch = make(chan int,0) close(ch) ch <- 108 }

运行结果:

panic: send on closed channel goroutine 1 [running]: main.main()

send on closed channel:发送给已关闭的通道。

5、死锁,所有线程睡眠(致命错误)

这一条是致命错误,不属于panic,但是跟panic异常一样,会导致程序挂掉。

package main
func main() {
    var ch chan int
    ch = make(chan int)
    ch <- 108
}

运行结果:

fatal error: all goroutines are asleep - deadlock! goroutine 1 [chan send]: main.main()

fatal error: all goroutines are asleep - deadlock:指明错误,所有线程睡眠,死锁。

6、给空map赋值

package main
func main() {
    var mm map[string]int
    mm["a"] = 10
}

运行结果

panic: assignment to entry in nil map goroutine 1 [running]: main.main()

assignment to entry in nil map:赋值到空的map。

7、致命错误:并发的map写

package main
import (
    "fmt"
    "time"
)
func main() {
    mm := make(map[string]int, 0)
    mm["jack"] = 20
    fmt.Println(mm)
    for i := 0; i < 10; i++ {
        name := fmt.Sprint(i)
        go addMap(mm,name,i)
    }
    time.Sleep(time.Second)
    fmt.Println(mm)
}
func addMap(m map[string]int, name string, age int) map[string]int {
    m[name] = age
    return m
}

运行结果

map[jack:20] fatal error: concurrent map writes goroutine 42 [running]: runtime.throw(0x10c8fe1, 0x15) /usr/local/Cellar/go/1.12.1/libexec/src/runtime/panic.go:617 +0x72 fp=0xc0000a6f18 sp=0xc0000a6ee8 pc=0x1027e52 runtime.mapassign_faststr(0x10ab2c0, 0xc000098000, 0x1162919, 0x1, 0x0) /usr/local/Cellar/go/1.12.1/libexec/src/runtime/map_faststr.go:211 +0x42a fp=0xc0000a6f80 sp=0xc0000a6f18 pc=0x100fe9a main.addMap(0xc000098000, 0x1162919, 0x1, 0x9, 0x0) /Users/ElonJelinek/go/src/kongyixueyuan.com/prac0424/test09.go:21 +0x4b fp=0xc0000a6fb8 sp=0xc0000a6f80 pc=0x1093c0b runtime.goexit()

concurrent map writes:并发map写。

Go语言中,map只能并发的读,不可以并发的写,如果要在多个线程中对map进行写操作,必须要加锁

8、递归死循环,堆栈溢出(运行时:goroutine堆栈超出限制,致命错误:堆栈溢出)

package main
import "fmt"
type People1 struct {
    Name string
}
func (p *People1) String() string {
    return fmt.Sprintf("print: %v", p)
}
func main() {
    p := &People1{"杰克"}
    s := p.String()
    fmt.Println(s)
}

运行结果

runtime: goroutine stack exceeds 1000000000-byte limit fatal error: stack overflow runtime stack: runtime.throw(0x10c8f32, 0xe) /usr/local/Cellar/go/1.12.1/libexec/src/runtime/panic.go:617 +0x72 runtime.newstack() /usr/local/Cellar/go/1.12.1/libexec/src/runtime/stack.go:1041 +0x6f0 runtime.morestack() /usr/local/Cellar/go/1.12.1/libexec/src/runtime/asm_amd64.s:429 +0x8f goroutine 1 [running]: runtime.heapBitsSetType(0xc013a67380, 0xc0, 0xb8, 0x10bdce0) /usr/local/Cellar/go/1.12.1/libexec/src/runtime/mbitmap.go:938 +0xa56 fp=0xc024000330 sp=0xc024000328 pc=0x1012b96 runtime.mallocgc(0xc0, 0x10bdce0, 0x1, 0x0)

这里出现了递归循环调用,即死循环,因为结构体people重写了官方包的String()方法,而fmt.Sprintf()中调用了String()方法,就出现了又回到p.String()方法,然后又调用fmt.Sprintf()方法,然后又回来,导致死循环,也导致堆栈溢出。

runtime: goroutine stack exceeds 1000000000-byte limit:运行时:goroutine堆栈超出1000000000字节限制

fatal error: stack overflow:

致命错误:堆栈溢出

这种错误在递归没有出口的时候出现,也就是递归死循环的时候出现

package main
import "fmt"
func main() {
    n := fibonacci(5)
    fmt.Println(n)
}
func fibonacci(n int) int {
    //if n <= 1 {
    //    return n
    //}
    return fibonacci(n-1) + fibonacci(n-2)
}

运行结果

runtime: goroutine stack exceeds 1000000000-byte limit fatal error: stack overflow runtime stack: runtime.throw(0x10c71e3, 0xe)

map并发加锁

package main
import (
    "fmt"
    "sync"
    "time"
)
func main() {
    mut := new(sync.Mutex)
    mm := make(map[string]int, 0)
    mm["jack"] = 20
    fmt.Println(mm)
    for i := 0; i < 20; i++ {
        name := fmt.Sprint(i)
        go addMap(mm,name,i,mut)
    }
    time.Sleep(time.Second)
    fmt.Println(mm)
}
func addMap(m map[string]int, name string, age int,mu *sync.Mutex) map[string]int {
    mu.Lock()
    m[name] = age
    mu.Unlock()
    return m
}

beego框架对运行异常的处理

2019-01-11 20:42:15 benben_2015open in new window 阅读数 1673更多

分类专栏: GO学习总结open in new window

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SAopen in new window 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/benben_2015/article/details/86317154open in new window

运行时异常

panic

在通常情况下,函数向其调用方报告错误的方式都是返回一个error类型的值。但是当遇到致命错误的时候,很可能会使程序无法继续运行。Go推荐通过调用panic函数来报告致命错误,它会停止当前控制流程,并引发一个运行时恐慌。例如:

package main
import "errors"
func main() {
    outerFunc()
}
func outerFunc() {
    innerFunc()
}
func innerFunc() {
    panic(errors.New("an intended fatal error"))
}

程序执行后的结果为:

D:\gotest>go run main.go panic: an intended fatal error goroutine 1 [running]: main.innerFunc(...) D:/gotest/main.go:14 main.outerFunc(...) D:/gotest/main.go:10 main.main() D:/gotest/main.go:6 +0x77 exit status 2

当调用innerFunc函数中的panic函数后,innerFunc的执行会被停止。紧接着,流程控制权会交给调用方outerFunc函数。然后,outerFunc函数的执行也会停止。运行时恐慌就沿着调用栈反方向进行传播,直至到达当前goroutine的调用栈的最顶层。一旦达到顶层,就意味着该goroutine调用栈中所有函数的执行都已经被停止了,程序已经崩溃。

当然,运行时恐慌并不都是通过调用panic函数的方式引发,也可以由Go的运行时系统来引发。例如发生像数组下标越界或类型断言失败这样的运行错误时,Go运行时会触发运行时panic。

recover

为了避免恐慌造成的程序崩溃。Go提供了专用于"拦截"运行时恐慌的内建函数recover,它可以使当前的程序从恐慌状态中恢复并重新获得流程控制权。recover函数被调用后,会返回一个interface{}类型的结果。如果当时的程序正处于运行时恐慌的状态,那么这个结果就会是非nil的。使用方法:

defer func() {
    if p := recover(); p != nil {
        fmt.Printf("Recovered panic: %s\n", p)
    }
}()

beego框架的处理

beego框架有一些初始化的函数,当你引用beego框架时,也就同时执行了初始化函数。(有关包的初始化,可以点击Go init函数详解open in new window进行查看)。其中beego\config.go文件中的init函数涉及到了程序对panic的处理。

func init () {
    BConfig = newBconfig()
    // ...
}
func newBConfig() *Config {
    return &Config{
        AppName:                "beego",
        RunMode:                DEV,
        RouterCaseSensitive:    true,
        ServerName:             "beegoServer:" + VERSION,
        RecoverPanic:           true,
        RecoverFunc:            recoverPanic,
        //...
    }
}
func recoverPanic(ctx *context.Context) {
    if err := recover(); err != nil {
        if err == ErrAbort {
            return
        }
        if !BConfig.RecoverPanic {
            panic(err)
        }
        if BConfig.EnableErrorsShow {
            if _,ok := ErrorMaps[fmt.Sprint(err)]; ok {
                exception(fmt.Sprint(err),ctx)
                return
            }
        }
        var stack string
        logs.Critical("the request url is ", ctx.Input.URL())
        logs.Critical(""Handler crashed with error", err)
        for i := 1; ; i++ {
            _, file, line, ok := runtime.Caller(i)
            if !ok {
                break
            }
            logs.Critical(fmt.Sprintf("%s:%d", file, line))
            stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line))
        }
        if BConfig.RunMode == DEV || BConfig.EnableErrorsRender {
            showErr(err, ctx, stack)
        }
    }
}

从上面的代码我们可以看到,使用beego框架启动程序时,会初始化一个recoverPanic方法。当程序遇到异常时,会进行异常恢复,防止程序崩溃。

beego在runmode模式为dev,且EnableErrorsRender(是否将错误信息进行渲染,默认值为true,即出错会提示友好的出错页面,对于API类型的应用,可能需要将该选项设置为false,以防止在dev模式下不必要的模板渲染信息返回)为true的情况下,会在前端将异常展示出来。而对于其他模式,beego只会在控制台将panic出现的调用栈信息依次打印出来。

参考文章

  1. Go并发编程实战open in new window
Last Updated:
Contributors: 刘荣杰