首页 攻略 技巧 致命的应用程序退出|Go 中有哪些难以恢复的致命场景?

致命的应用程序退出|Go 中有哪些难以恢复的致命场景?

更新时间:2022-09-05 17:04:37 分类:技巧 浏览:139

大家好,我是烤鱼。

在车祸现场,紧急恢复后,他正在检查代码一段时间。回头一看,这个错误提示明显是致命错误,还是定位比较好。

但此时,他实际上是在检查恐慌——如果他在那里错过了,我说这是一个很大的轰动......

明天烤鱼会和大家分享错误的类型以及触发的场景。

错误类型错误

第一个是 Go 中最标准的错误错误,它的实体是 {}。

如下:

  1. type error interface { 
  2.     Error() string 

在日常项目中,我们只需要创建任意结构体并实现Error方法致命的应用程序退出,就可以认为是error错误类型。

如下:

  1. type errorString struct { 
  2.     s string 
  3.  
  4. func (e *errorString) Error() string { 
  5.     return e.s 

对外调用标准库API,通常如下:

  1. f, err := os.Open("filename.ext"
  2. if err != nil { 
  3.     log.Fatal(err) 
  4. // do something with the open *File f 

我们会同意最后一个参数是错误类型,通常在第二个参数中常见,可以有一个习惯。

恐慌

第二个是Go中的异常处理panic,会形成异常错误。结合panic+,可以反转程序的运行状态。

如下:

  1. package main 
  2.  
  3. import "os" 
  4.  
  5. func main() { 
  6.     panic("a problem"
  7.  
  8.     _, err := os.Create("/tmp/file"
  9.     if err != nil { 
  10.         panic(err) 
  11.     } 

输出结果:

  1. $ go run panic.go 
  2. panic: a problem 
  3. goroutine 1 [running]: 
  4. main.main() 
  5.     /.../panic.go:12 +0x47 
  6. ... 
  7. exit status 2 

如果不用作捕获,程序将被中断。所以经常被误认为是程序中断,100%是由panic引起的。

这是个误会。

投掷

Go初学者经常踩到但不知道的第三种错误是致命错误抛出。

这种错误类型不能在用户端主动调用。它是由Go本身底层调用的,比如你常见的map并发读写,就是由this触发的。

源码如下:

  1. func throw(s string) { 
  2.  systemstack(func() { 
  3.   print("fatal error: ", s, "n"
  4.  }) 
  5.  gp := getg() 
  6.  if gp.m.throwing == 0 { 
  7.   gp.m.throwing = 1 
  8.  } 
  9.  fatalthrow() 
  10.  *(*int)(nil) = 0 // not reached 

根据上述过程,将获得G的当前实例,并将其M的状态设置为1。

设置状态后,会调用该方法进行真正的crash相关操作:

  1. func fatalthrow() { 
  2.  pc := getcallerpc() 
  3.  sp := getcallersp() 
  4.  gp := getg() 
  5.   
  6.  systemstack(func() { 
  7.   startpanic_m() 
  8.   if dopanic_m(gp, pc, sp) { 
  9.    crash() 
  10.   } 
  11.  
  12.   exit(2) 
  13.  }) 
  14.  
  15.  *(*int)(nil) = 0 // not reached 

主要逻辑是发送信号量,最后调用exit方法退出,所以你会发现这是一个不可阻挡的“致命”错误。

致命的应用程序退出|Go 中有哪些难以恢复的致命场景?
致命的应用程序退出|Go 中有哪些难以恢复的致命场景?

致命一幕

因此,作为一个“成熟”的围棋工程师,我不仅要保证自己程序的健壮性,还收集了网上一些致命的错误场景,分享给大家。

让我们一起学习,避免这种致命的场景,争取在年底拿到A,不要背着P0车祸。

并发读写map

  1. func foo() { 
  2.  m := map[string]int{} 
  3.  go func() { 
  4.   for { 
  5.    m["煎鱼1"] = 1 
  6.   } 
  7.  }() 
  8.  for { 
  9.   _ = m["煎鱼2"
  10.  } 

输出结果:

  1. fatal error: concurrent map read and map write 
  2.  
  3. goroutine 1 [running]: 
  4. runtime.throw(0x1078103, 0x21) 
  5. ... 

堆栈内存不足

  1. func foo() { 
  2.  var f func(a [1000]int64) 
  3.  f = func(a [1000]int64) { 
  4.   f(a) 
  5.  } 
  6.  f([1000]int64{}) 

输出结果:

  1. runtime: goroutine stack exceeds 1000000000-byte limit 
  2. runtime: sp=0xc0200e1bf0 stack=[0xc0200e0000, 0xc0400e0000] 
  3. fatal error: stack overflow 
  4.  
  5. runtime stack: 
  6. runtime.throw(0x1074ba3, 0xe) 
  7.         /usr/local/Cellar/go/1.16.6/libexec/src/runtime/panic.go:1117 +0x72 
  8. runtime.newstack() 
  9. ... 

使用 nil 函数作为启动

  1. func foo() { 
  2.  var f func() 
  3.  go f() 

输出结果:

  1. fatal error: go of nil func value 
  2.  
  3. goroutine 1 [running]: 
  4. main.foo() 
  5. ... 

死锁

  1. func foo() { 
  2.  select {} 

输出结果:

  1. fatal error: all goroutines are asleep - deadlock! 
  2.  
  3. goroutine 1 [select (no cases)]: 
  4. main.foo() 
  5. ... 

线程限制已用完

如果你的被 IO 操作阻塞,可能会启动一个新线程来执行你的另一个。

Go 对最大线程数有一个默认限制,如果达到此限制,您的应用程序将崩溃。

会出现以下输出:

  1. fatal error: thread exhaustion 
  2. ... 

调用.可以减少线程数,但也要检查程序是否有问题。

超过可用的视频内存

如果执行操作,如:下载大文件等,导致应用占用显存过多,程序掉线,导致OOM。

会出现以下输出:

  1. fatal error: runtime: out of memory 
  2. ... 

建议删除一些程序或购买新的笔记本电脑。

总结

在明天的文章中,我们将介绍 Go 中的三种类型的错误。其中,介绍了致命错误致命的应用程序退出,这种错误最为罕见,但一旦遇到很容易倾覆,并给出了一些经典案例。

我希望你以后可以避免它。你有遇到过这些场景吗?

版权声明: 本站内容部分来源网络,版权归作者所有,如有侵权,请联系我们删除!