likes
comments
collection
share

Golang中关于Panic的俩个易错点

作者站长头像
站长
· 阅读数 18

在日常开发中一不小心程序就会出现panic,如果没有注册recoverpanic会直接中断程序后面的逻辑,使用不当会带来巨大的隐患。下面小老虎就来介绍俩点关于panic的常见错误!

一、Panic与Recover

在并发问题中,我们常常使用读写锁来保证并发的安全性。在内存泄漏的七种场景中我们提到锁的使用不当,会使得groutine因获取不到锁,而导致内存泄漏。下面以超超和婷婷获取电视使用权为例。

定义电视结构体并提供注册和获取当前使用者的方法

type Television struct {
  belong string
  sync.RWMutex
}
​
func (m *Television) set(name string) {
  m.Lock()
  m.belong = name
  m.Unlock()
}
​
func (m *Television) get() string {
  m.RLock()
  user := m.belong
  m.RUnlock()
  return user
}

主进程中用户并发获取电视机使用权

func main() {
  users := []string{"chaochao", "tingting"}
  tv := &Television{}
  w := sync.WaitGroup{}
  usersLen := len(users)
​
  w.Add(usersLen)
  for i := 0; i < usersLen; i++ {
    go func(user string) {
      tv.set(user)
      w.Done()
    }(users[i])
  }
  w.Wait()
  fmt.Println("TV user is", tv.get())
}

输出结果

TV user is tingting

看似没有任何问题,如果get方法和set方法再复杂一些,中间不小心出了panic,会怎么样呢?

func (m *Television) set(name string) {
  m.Lock()
  m.belong = name
  //模拟出现的panic
  panic("setErr")
  m.Unlock()
}

那么整个程序都会崩溃,线上如果出现这样的问题,基本属于中大奖了!因此需要注册一个deferrecover这个panic,使得程序能够继续运行。

func main() {
  users := []string{"chaochao", "tingting"}
  tv := &Television{}
  w := sync.WaitGroup{}
  usersLen := len(users)
​
  w.Add(usersLen)
  for i := 0; i < usersLen; i++ {
    go func(user string) {
      //获取处理异常
      defer func() {
        if err := recover(); err != nil {
          fmt.Println("err:", err)
        }
      }()
      tv.set(user)
      w.Done()
    }(users[i])
  }
  w.Wait()
  fmt.Println("TV user is", tv.get())
}

看似程序到这没有问题了,但是真是如此吗?

二、内存泄漏

set方法中,panic之前有一个获取锁的操作,panic之后set方法直接退出了,并没有释放锁。这时其他协程在尝试获取锁时就会失败,从而造成Goroutine的泄漏。

Golang中关于Panic的俩个易错点因此锁的释放最好在lock后注册一个defer进行释放。

type Television struct {
  belong string
  sync.RWMutex
}
​
func (m *Television) set(name string) {
  m.Lock()
  defer m.Unlock()
  m.belong = name
}
​
func (m *Television) get() string {
  m.RLock()
  defer m.RUnlock()
  user := m.belong
  return user
}
三、总结

本文介绍了panicrecover的使用场景,recover通常放在defer中防止panic中断程序给线上带来巨大的影响。紧接着介绍了锁的释放最好也放在defer中,防止painic时未释放锁导致其他协程未拿到锁而导致内存泄漏。关于painic更多的注意事项,欢迎小伙伴们在下方留言讨论呀!