在日常开发中一不小心程序就会出现panic
,如果没有注册recover
,panic
会直接中断程序后面的逻辑,使用不当会带来巨大的隐患。下面小老虎就来介绍俩点关于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()
}
那么整个程序都会崩溃,线上如果出现这样的问题,基本属于中大奖了!因此需要注册一个defer
去recover
这个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
的泄漏。
因此锁的释放最好在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
}
三、总结
本文介绍了panic
和recover
的使用场景,recover
通常放在defer
中防止panic
中断程序给线上带来巨大的影响。紧接着介绍了锁的释放最好也放在defer
中,防止painic
时未释放锁导致其他协程未拿到锁而导致内存泄漏。关于painic
更多的注意事项,欢迎小伙伴们在下方留言讨论呀!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。