在日常开发中一不小心程序就会呈现 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
更多的注意事项,欢送小伙伴们在下方留言探讨呀!