序
本文次要钻研一下 gost 的 GoSafely
GoSafely
gost/runtime/goroutine.go
func GoSafely(wg *sync.WaitGroup, ignoreRecover bool, handler func(), catchFunc func(r interface{})) {
if wg != nil {wg.Add(1)
}
go func() {defer func() {//......}()
handler()}()}
GoSafely 接管 WaitGroup、ignoreRecover、handler、catchFunc 参数,其大抵的模板是,首先对 WaitGroup 进行 add(1),而后一步执行带 defer 的 handler
defer
gost/runtime/goroutine.go
defer func() {if r := recover(); r != nil {
if !ignoreRecover {
fmt.Fprintf(os.Stderr, "%s goroutine panic: %v\n%s\n",
time.Now(), r, string(debug.Stack()))
}
if catchFunc != nil {//......}
}
if wg != nil {wg.Done()
}
}()
GoSafely 的 defer 先执行 recover(),而后依据 ignoreRecover 判断是否打印 err,最初解决 WaitGroup,与一般 recover 不同的是多了一个 catchFunc 解决
catchFunc
gost/runtime/goroutine.go
if catchFunc != nil {
if wg != nil {wg.Add(1)
}
go func() {defer func() {if p := recover(); p != nil {
if !ignoreRecover {
fmt.Fprintf(os.Stderr, "recover goroutine panic:%v\n%s\n",
p, string(debug.Stack()))
}
}
if wg != nil {wg.Done()
}
}()
catchFunc(r)
}()}
catchFunc 算是一个 mini 版的 GoSafely,先执行 wg.Add(1),再异步执行 func,异步 func 外头先注册 defer,解决 recover 及 wg,而后执行 catchFunc
实例
gost/runtime/goroutine_test.go
func TestGoSafe(t *testing.T) {times := int32(1)
var wg sync.WaitGroup
GoSafely(&wg,
false,
func() {panic("hello")
},
func(r interface{}) {atomic.AddInt32(×, 1)
},
)
wg.Wait()
assert.True(t, atomic.LoadInt32(×) == 2)
GoSafely(nil,
false,
func() {panic("hello")
},
func(r interface{}) {atomic.AddInt32(×, 1)
},
)
time.Sleep(1e9)
assert.True(t, atomic.LoadInt32(×) == 3)
}
这里模仿了一下 handler 产生 panic,一个有 WaitGroup,一个没有 WaitGroup 的场景
小结
gost 提供了 GoSafely 办法,它接管 WaitGroup、ignoreRecover、handler、catchFunc 参数,其大抵的模板是,首先对 WaitGroup 进行 add(1),而后一步执行带 defer 的 handler。catchFunc 算是一个 mini 版的 GoSafely,先执行 wg.Add(1),再异步执行 func,异步 func 外头先注册 defer,解决 recover 及 wg,而后执行 catchFunc。
doc
- gost