大家好,我是建宇。在事故现场,紧急恢复后,他正在检查代码很长时间。回头一看,这个报错信息明显是致命错误,还是定位一下比较好。但是这个时候他居然在检查panic-recover是不是少了,吓我一跳……今天建宇就给大家分享一下错误的类型以及触发的场景。错误类型error第一种是Go中最标准的错误error,其实就是一个interface{}。如下:typeerrorinterface{Error()string}在日常工程中,我们只需要创建任意结构体并实现Error方法,就可以认为是错误类型。如下:typeerrorStringstruct{sstring}func(e*errorString)Error()string{returne.s}对外调用标准库API,一般如下:f,err:=os.Open("filename.ext")iferr!=nil{log.Fatal(err)}//dosomethingwiththeopen*Filef我们会约定最后一个参数是错误类型,一般常见于第二个参数,可以有一个习惯的习惯。第二种panic是Go中的异常处理panic,可以产生异常错误,结合panic+recover可以逆转程序的运行状态。如下:packagemainimport"os"funcmain(){panic("aproblem")_,err:=os.Create("/tmp/file")iferr!=nil{panic(err)}}输出结果:$gorunpanic.gopanic:aproblemgoroutine1[running]:main.main()/.../panic.go:12+0x47...exitstatus2如果不使用recover作为捕获,程序将被打断。因此,人们常常误认为程序中断100%是恐慌造成的。这是一种误解。第三种throw是Go初学者经常踩到不知道的一类错误,也就是致命错误throw。此类错误无法在用户端主动调用。它由Go本身的底层调用。比如常见的map的并发读写就是由此触发的。其源码如下:functhrow(sstring){systemstack(func(){print("fatalerror:",s,"\n")})gp:=getg()ifgp.m.throwing==0{gp.m.throwing=1}fatalthrow()*(*int)(nil)=0//notreached}根据上面的程序,会得到G的当前实例及其M的throwing状态将设置为1。设置状态后,将调用fatalthrow方法执行真正的崩溃相关操作:funcfatalthrow(){pc:=getcallerpc()sp:=getcallersp()gp:=getg()systemstack(func(){startpanic_m()ifdopanic_m(gp,pc,sp){crash()}exit(2)})*(*int)(nil)=0//notreached}主要逻辑是发送_SIGABRT信号量,最后调用exit方法退出,所以你会发现这是一个无法停止的“致命”错误。致命场景为此,作为一个“成熟”的Go工程师,除了保证自己程序的健壮性,我也在网上收集了一些致命错误场景,分享给大家。一起学习并避免这些致命的情况,以在年底获得A为目标,不要背上P0事故。并发读写mapfuncfoo(){m:=map[string]int{}gofunc(){for{m["FriedFish1"]=1}}()for{_=m["FriedFish2"]}}输出结果:fatalerror:concurrentmapreadandmapwritegoroutine1[running]:runtime.throw(0x1078103,0x21)...stackmemoryexhaustedfuncfoo(){varffunc(a[1000]int64)f=func(a[1000]int64){f(a)}f([1000]int64{})}输出结果:runtime:goroutinestackexceeds1000000000-bytelimitruntime:sp=0xc0200e1bf0stack=[0xc0200e0000,0xc0400e0000]致命错误:堆栈溢出运行时堆栈:runtime.throw(0x1074ba3,0xe)/usr/local/Cellar/go/1.16.6/libexec/src/runtime/panic.go:1117+0x72runtime.newstack()...willnilfunctionStartfuncfoo()asagoroutine{varffunc()gof()}输出结果:fatalerror:goofnilfuncvaluegoroutine1[running]:main.foo()...goroutinesdeadlockfuncfoo(){select{}}输出:致命错误:所有goroutines都睡着了——死锁!goroutine1[select(nocases)]:main.foo()...threadlimitexhausted如果你的goroutines被IO操作阻塞,new可以启动以执行其他goroutine的最大线程数有默认限制,如果达到此限制,您的应用程序将崩溃。会出现如下输出:fatalerror:threadexhaustion...可以通过调用runtime.SetMaxThreads方法增加线程数,但是也需要考虑是不是程序有问题。Exceedingavailablememory如果你执行如:下载大文件等操作,导致应用程序占用内存过多,程序上升,导致OOM。会出现如下输出:fatalerror:runtime:outofmemory...建议处理掉一些程序,或者更换新电脑。总结在今天的文章中,我们介绍了Go语言中的三种错误类型。其中介绍了大家最不常见,但遇到容易翻车的致命错误,并给出了一些经典案例。希望大家以后都能避免。你遇到过这样的场景吗?如有任何问题,欢迎在评论区反馈交流。最好的关系是相互成就。您的好评是创作炸鱼最大的动力。感谢您的支持。文章持续更新中。可以微信搜索【脑补炸鱼】阅读。本文已收录在GitHubgithub.com/eddycjy/blog中。学习Go语言可以看Go学习地图和路线。欢迎星星提醒。请参阅Go中是否可以恢复所有运行时错误?
